// swad_xml.c: XML file generation and parsing /* SWAD (Shared Workspace At a Distance), is a web platform developed at the University of Granada (Spain), and used to support university teaching. This file is part of SWAD core. Copyright (C) 1999-2024 Antonio Caņas Vargas This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ /*****************************************************************************/ /********************************* Headers ***********************************/ /*****************************************************************************/ #include // For isspace() #include // For NULL #include // For free () #include // For strlen (), etc. #include "swad_changelog.h" #include "swad_error.h" #include "swad_global.h" #include "swad_HTML.h" #include "swad_xml.h" /*****************************************************************************/ /************** External global variables from others modules ****************/ /*****************************************************************************/ extern struct Globals Gbl; /*****************************************************************************/ /************************* Private global variables **************************/ /*****************************************************************************/ static const char *XML_Ptr; /*****************************************************************************/ /***************************** Private prototypes ****************************/ /*****************************************************************************/ static void XML_GetElement (struct XMLElement *ParentElem); static void XML_GetAttributes (struct XMLElement *Elem); static void XML_SkipSpaces (void); /*****************************************************************************/ /****** Write the start of an XML file with author and date of creation ******/ /*****************************************************************************/ void XML_WriteStartFile (FILE *FileTgt,const char *Type,bool Credits) { extern const char *Txt_NEW_LINE; fprintf (FileTgt,"%s" "<%s>%s", Txt_NEW_LINE, Type,Txt_NEW_LINE); if (Credits) fprintf (FileTgt,"%s%s" "%s%s" "%ld%s", Log_PLATFORM_VERSION,Txt_NEW_LINE, Gbl.Usrs.Me.UsrDat.FullName,Txt_NEW_LINE, time (NULL),Txt_NEW_LINE); } /*****************************************************************************/ /********************* Write the end of an XML file **************************/ /*****************************************************************************/ void XML_WriteEndFile (FILE *FileTgt,const char *Type) { extern const char *Txt_NEW_LINE; fprintf (FileTgt,"%s",Type,Txt_NEW_LINE); } /*****************************************************************************/ /****************** Get the content of XML root element **********************/ /*****************************************************************************/ void XML_GetTree (const char *XMLBuffer,struct XMLElement **XMLRootElem) { /***** Allocate space for the root element *****/ if ((*XMLRootElem = calloc (1,sizeof (**XMLRootElem))) == NULL) Err_NotEnoughMemoryExit (); XML_Ptr = XMLBuffer; XML_GetElement (*XMLRootElem); } /*****************************************************************************/ /******************* Get the content of an XML element ***********************/ /*****************************************************************************/ static void XML_GetElement (struct XMLElement *ParentElem) { struct XMLElement *ChildElem; const char *StartContent; const char *Ptr; size_t ContentLength; size_t EndTagNameLength; size_t TagLength; char ErrorTxt[128]; /* element content ^ XML_Ptr */ /* Skip spaces */ XML_SkipSpaces (); StartContent = XML_Ptr; ContentLength = strcspn (StartContent,"<"); XML_Ptr += ContentLength; while (*XML_Ptr == '<') // For each child until parent end tag { XML_Ptr++; if (*XML_Ptr == '/') // Parent end tag { /* element content ^ XML_Ptr */ /***** Check tag name *****/ XML_Ptr++; /* element content ^ XML_Ptr */ EndTagNameLength = strcspn (XML_Ptr,">"); if (ParentElem->TagNameLength != EndTagNameLength) { snprintf (ErrorTxt,sizeof (ErrorTxt), "XML syntax error. Expect end tag </%s>.", ParentElem->TagName); Err_ShowErrorAndExit (ErrorTxt); } if (strncmp (ParentElem->TagName,XML_Ptr,EndTagNameLength)) // XML tags are case sensitive { snprintf (ErrorTxt,sizeof (ErrorTxt), "XML syntax error. Expect end tag </%s>.", ParentElem->TagName); Err_ShowErrorAndExit (ErrorTxt); } // End of parent element found! XML_Ptr += EndTagNameLength; XML_Ptr++; /* element content ^ XML_Ptr */ /* Remove trailing spaces in content */ for (Ptr = StartContent + ContentLength - 1; ContentLength; ContentLength--, Ptr--) if (!isspace ((int) *Ptr)) break; /* Copy content */ if (ContentLength) { if ((ParentElem->Content = malloc (ContentLength + 1)) == NULL) Err_NotEnoughMemoryExit (); strncpy (ParentElem->Content,StartContent,ContentLength); ParentElem->Content[ContentLength] = '\0'; ParentElem->ContentLength = ContentLength; } return; } else if (*XML_Ptr == '!' || *XML_Ptr == '?') // Skip and { TagLength = strcspn (XML_Ptr,">"); XML_Ptr += TagLength; if (*XML_Ptr == '>') XML_Ptr++; } else // New start tag { /* ...... ^ XML_Ptr */ /***** Allocate space for the child element *****/ if ((ChildElem = calloc (1,sizeof (*ChildElem))) == NULL) Err_NotEnoughMemoryExit (); /***** Adjust XML elements pointers *****/ if (ParentElem->FirstChild) // This child is a brother of a former child ParentElem->LastChild->NextBrother = ChildElem; else // This child is the first child ParentElem->FirstChild = ChildElem; ParentElem->LastChild = ChildElem; /***** Get child tag name *****/ ChildElem->TagNameLength = strcspn (XML_Ptr,">/ \t"); if ((ChildElem->TagName = malloc (ChildElem->TagNameLength + 1)) == NULL) Err_NotEnoughMemoryExit (); strncpy (ChildElem->TagName,XML_Ptr,ChildElem->TagNameLength); ChildElem->TagName[ChildElem->TagNameLength] = '\0'; XML_Ptr += ChildElem->TagNameLength; /* ...... ^ XML_Ptr */ /* Check if end of start tag found */ if (*XML_Ptr == '>') // End of start tag { XML_Ptr++; /* ...... ^ XML_Ptr */ XML_GetElement (ChildElem); } else if (*XML_Ptr == '/') // Unary tag? { XML_Ptr++; /* ... ^ XML_Ptr */ if (*XML_Ptr != '>') // Here it should be the end of start tag Err_ShowErrorAndExit ("XML syntax error. Expect > ending unary tag."); XML_Ptr++; /* ... ^ XML_Ptr */ } else // Begin of an attribute { /* ...... ^ XML_Ptr */ XML_GetAttributes (ChildElem); /* ...... ^ XML_Ptr */ XML_GetElement (ChildElem); } } /* Skip spaces after the ">" character of the tag */ XML_SkipSpaces (); } } /*****************************************************************************/ /******************* Get the attributes of a start tag ***********************/ /*****************************************************************************/ static void XML_GetAttributes (struct XMLElement *Elem) { struct XMLAttribute *Attribute; bool EndOfStartTag = false; char ErrorTxt[256]; /* ...... ^ | XML_Ptr */ do { /* Skip spaces */ XML_SkipSpaces (); if (*XML_Ptr == '/') // End of unary tag? { XML_Ptr++; if (*XML_Ptr == '>') { XML_Ptr++; EndOfStartTag = true; } else Err_ShowErrorAndExit ("XML syntax error. Expect > ending unary tag with attributes."); } else if (*XML_Ptr == '>') // End of start tag? { XML_Ptr++; EndOfStartTag = true; } else if (*XML_Ptr == '\0') Err_ShowErrorAndExit ("XML syntax error. Unexpected end of file."); else { /* Begin of attribute name: ...... ^ | XML_Ptr */ /***** Allocate space for the attribute *****/ if ((Attribute = calloc (1,sizeof (*Attribute))) == NULL) Err_NotEnoughMemoryExit (); /***** Adjust XML element and attribute pointers *****/ if (Elem->FirstAttribute) // This attribute is a brother of a former attribute in current element Elem->LastAttribute->Next = Attribute; else // This child is the first child Elem->FirstAttribute = Attribute; Elem->LastAttribute = Attribute; /***** Get attribute name *****/ Attribute->AttributeNameLength = strcspn (XML_Ptr,"="); if ((Attribute->AttributeName = malloc (Attribute->AttributeNameLength + 1)) == NULL) Err_NotEnoughMemoryExit (); strncpy (Attribute->AttributeName,XML_Ptr,Attribute->AttributeNameLength); Attribute->AttributeName[Attribute->AttributeNameLength] = '\0'; XML_Ptr += Attribute->AttributeNameLength; /* End of attribute name: ...... ^ | XML_Ptr */ /***** Get attribute content *****/ XML_Ptr++; if (*XML_Ptr == '\"') { XML_Ptr++; Attribute->ContentLength = strcspn (XML_Ptr,"\""); } else if (*XML_Ptr == '\'') { XML_Ptr++; Attribute->ContentLength = strcspn (XML_Ptr,"'"); } else { snprintf (ErrorTxt,sizeof (ErrorTxt), "XML syntax error after attribute "%s"" " inside element "%s".", Attribute->AttributeName,Elem->TagName); Err_ShowErrorAndExit (ErrorTxt); } if ((Attribute->Content = malloc (Attribute->ContentLength + 1)) == NULL) Err_NotEnoughMemoryExit (); strncpy (Attribute->Content,XML_Ptr,Attribute->ContentLength); Attribute->Content[Attribute->ContentLength] = '\0'; XML_Ptr += Attribute->ContentLength; XML_Ptr++; /* End of attribute content ...... ^ | XML_Ptr */ } } while (!EndOfStartTag); } /*****************************************************************************/ /****************** Skip spaces while parsing XML buffer *********************/ /*****************************************************************************/ static void XML_SkipSpaces (void) { while (isspace ((int) *XML_Ptr)) XML_Ptr++; } /*****************************************************************************/ /**************************** Print an XML element ***************************/ /*****************************************************************************/ void XML_PrintTree (struct XMLElement *ParentElem) { static int Level = -1; struct XMLElement *ChildElem; struct XMLElement *NextBrother; struct XMLAttribute *Attribute; int i; Level++; /***** Print start tag *****/ if (Level > 0) { for (i = 1; i < Level; i++) HTM_Txt (" "); HTM_TxtF ("<%s",ParentElem->TagName ? ParentElem->TagName : ""); /* Print attributes */ for (Attribute = ParentElem->FirstAttribute; Attribute != NULL; Attribute = Attribute->Next) HTM_TxtF (" %s="%s"", Attribute->AttributeName, Attribute->Content); HTM_Txt (">\n"); /***** Print content *****/ if (ParentElem->Content) { for (i = 1; i < Level; i++) HTM_Txt (" "); HTM_TxtF ("%s\n",ParentElem->Content); } } /***** Print children *****/ ChildElem = ParentElem->FirstChild; while (ChildElem) { NextBrother = ChildElem->NextBrother; XML_PrintTree (ChildElem); ChildElem = NextBrother; } /***** Print end tag *****/ if (Level > 0) { for (i = 1; i < Level; i++) HTM_Txt (" "); HTM_TxtF ("</%s>\n",ParentElem->TagName ? ParentElem->TagName : ""); } Level--; } /*****************************************************************************/ /********** Get attribute "yes"/"no" from an XML element in a tree ***********/ /*****************************************************************************/ bool XML_GetAttributteYesNoFromXMLTree (struct XMLAttribute *Attribute) { if (!Attribute->Content) Err_ShowErrorAndExit ("XML attribute yes/no not found."); if (!strcasecmp (Attribute->Content,"yes") || !strcasecmp (Attribute->Content,"y")) // case insensitive, because users can edit XML return true; if (!strcasecmp (Attribute->Content,"no") || !strcasecmp (Attribute->Content,"n")) // case insensitive, because users can edit XML return false; Err_ShowErrorAndExit ("XML attribute yes/no not found."); return false; // Not reached } /*****************************************************************************/ /************ Free the memory allocated for an XML element *******************/ /*****************************************************************************/ void XML_FreeTree (struct XMLElement *ParentElem) { struct XMLElement *ChildElem; struct XMLElement *NextElemBrother; struct XMLAttribute *Attribute; struct XMLAttribute *NextAttrib; /***** Free memory allocated for children *****/ ChildElem = ParentElem->FirstChild; while (ChildElem) { NextElemBrother = ChildElem->NextBrother; XML_FreeTree (ChildElem); ChildElem = NextElemBrother; } /***** Free memory allocated for TagName *****/ if (ParentElem->TagName) { free (ParentElem->TagName); ParentElem->TagName = NULL; ParentElem->TagNameLength = (size_t) 0; } /***** Free memory allocated for attributes *****/ Attribute = ParentElem->FirstAttribute; while (Attribute) { NextAttrib = Attribute->Next; free (Attribute->AttributeName); free (Attribute->Content); free (Attribute); Attribute = NextAttrib; } /***** Free memory allocated for Content *****/ if (ParentElem->Content) { free (ParentElem->Content); ParentElem->Content = NULL; ParentElem->ContentLength = (size_t) 0; } }