// swad_web_service.c: SWAD web service provided to external plugins /* 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-2019 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 . */ /*****************************************************************************/ /******* How to use the gSOAP toolkit to create a web-service server *********/ /*****************************************************************************/ /* This code uses the gSOAP toolkit: http://www.cs.fsu.edu/~engelen/soap.html http://gsoap2.sourceforge.net/ Version 2.8.8 (19/02/2012) To install the toolkit: 1. Install g++ if not installed: yum -y install gcc-c++ 2. Install yacc if not installed: yum -y install byacc 3. Install flex if not installed: yum -y install flex 4. Install bison if not installed: yum -y install bison 5. Install bison-devel if not installed: yum -y install bison-devel 6. unzip gsoap_2.8.8.zip 7. cd gsoap-2.8 8. ./configure 9. make 10. make install Steps to generate swad.wsdl: 1. Go to directory soap cd /home/acanas/swad/swad/soap 2. Inside soap, create a C header file with the web service. Example swad_web_service.h: ---------- // File: swad_web_service.h //gsoap swad service name: swad //gsoap swad service namespace: urn:swad //gsoap swad service location: https://swad.ugr.es/ ... ---------- 3. Inside soap, execute soapcpp2 compiler: soapcpp2 -c -S swad_web_service.h 4. Copy generated WSDL file (swad.wsdl) to public location (probably you must be root): cp -f /home/acanas/swad/swad/soap/swad.wsdl /var/www/html/ws/ 5. Put a '#include "soap/soapH.h"' line into the code of the web-service server (this file). 6. Put a '#include "soap/swad.nsmap"' into the code of the web-service server (this file). 7. Compile swad including the web service. The makefile must include -lgsoap, and compile soapC.c and soapServer.c files generated by soapcpp2 in step 2 Example of Makefile: --------------------- OBJS = list of swad object files SOAPOBJS = soap/soapC.o soap/soapServer.o CC = gcc LIBS = -lmysqlclient -lz -L/usr/lib64/mysql -lm -lgsoap CFLAGS = -Wall -O2 -s swad: $(OBJS) $(SOAPOBJS) $(CC) $(CFLAGS) -o $@ $(OBJS) $(SOAPOBJS) $(LIBS) chmod a+x $@ --------------------- 8. Copy CGI (swad) to the cgi directory: cp -f /home/acanas/swad/swad/swad /var/www/cgi-bin/ */ /*****************************************************************************/ /********************************* Headers ***********************************/ /*****************************************************************************/ // In Eclipse, add to include path /usr/include, /usr/local/include, /usr/include/i386-linux-gnu #include // For scandir, etc. #include // For PATH_MAX #include // For NULL #include // For lstat #include #include #include "soap/soapH.h" // gSOAP header #include "soap/swad.nsmap" // Namespaces map used #include "swad_account.h" #include "swad_database.h" #include "swad_file_browser.h" #include "swad_global.h" #include "swad_hierarchy.h" #include "swad_ID.h" #include "swad_notice.h" #include "swad_notification.h" #include "swad_password.h" #include "swad_search.h" #include "swad_user.h" #include "swad_web_service.h" #include "swad_xml.h" /*****************************************************************************/ /************** External global variables from others modules ****************/ /*****************************************************************************/ extern struct Globals Gbl; extern const char Str_BIN_TO_BASE64URL[64 + 1]; /*****************************************************************************/ /***************************** Private constants *****************************/ /*****************************************************************************/ // Add new functions at the end static const char *Svc_Functions[1 + Svc_NUM_FUNCTIONS] = { "?", // 0 ==> unknown function "loginBySession", // 1 "loginByUserPassword", // 2 (deprecated) "loginByUserPasswordKey", // 3 "getCourses", // 4 "getUsers", // 5 "getNotifications", // 6 "getTestConfig", // 7 "getTests", // 8 "sendMessage", // 9 "sendNotice", // 10 "getDirectoryTree", // 11 "getGroups", // 12 "getGroupTypes", // 13 "sendMyGroups", // 14 "getFile", // 15 "markNotificationsAsRead", // 16 "getNewPassword", // 17 "getCourseInfo", // 18 "getAttendanceEvents", // 19 "sendAttendanceEvent", // 20 "getAttendanceUsers", // 21 "sendAttendanceUsers", // 22 "createAccount", // 23 "getMarks", // 24 "getTrivialQuestion", // 25 "findUsers", // 26 "removeAttendanceEvent", // 27 }; /* Web service roles (they do not match internal swad-core roles) */ #define Svc_NUM_ROLES 4 typedef enum { Svc_ROLE_UNKNOWN = 0, // User not logged in Svc_ROLE__GUEST_ = 1, // User not belonging to any course Svc_ROLE_STUDENT = 2, // Student in current course Svc_ROLE_TEACHER = 3, // Teacher in current course } Svc_Role_t; /* Translation from service-web-role to swad-core-role */ Rol_Role_t Svc_SvcRole_to_RolRole[Svc_NUM_ROLES] = { Rol_UNK, // Svc_ROLE_UNKNOWN Rol_GST, // Svc_ROLE__GUEST_ Rol_STD, // Svc_ROLE_STUDENT Rol_TCH, // Svc_ROLE_TEACHER // TODO: Create new web service role for non-editing teachers }; /* Translation from swad-core-role to service-web-role */ Svc_Role_t Svc_RolRole_to_SvcRole[Rol_NUM_ROLES] = { Svc_ROLE_UNKNOWN, // Rol_UNK Svc_ROLE__GUEST_, // Rol_GST Svc_ROLE_UNKNOWN, // Rol_USR Svc_ROLE_STUDENT, // Rol_STD Svc_ROLE_TEACHER, // Rol_NET // TODO: Create new web service role for non-editing teachers Svc_ROLE_TEACHER, // Rol_TCH Svc_ROLE_UNKNOWN, // Rol_DEG_ADM Svc_ROLE_UNKNOWN, // Rol_CTR_ADM Svc_ROLE_UNKNOWN, // Rol_INS_ADM Svc_ROLE_UNKNOWN, // Rol_SYS_ADM }; #define Svc_BYTES_WS_KEY Cry_BYTES_ENCRYPTED_STR_SHA256_BASE64 /*****************************************************************************/ /********************************* Data types ********************************/ /*****************************************************************************/ /*****************************************************************************/ /***************************** Private prototypes ****************************/ /*****************************************************************************/ static int Svc_GetPlgCodFromAppKey (const char *appKey); static int Svc_CheckIdSession (const char *IdSession); static int Svc_CheckWSKey (char WSKey[Svc_BYTES_WS_KEY + 1]); static int Svc_CheckCourseAndGroupCodes (long CrsCod,long GrpCod); static int Svc_GenerateNewWSKey (long UsrCod, char WSKey[Svc_BYTES_WS_KEY + 1]); static int Svc_RemoveOldWSKeys (void); static int Svc_GetCurrentDegCodFromCurrentCrsCod (void); static bool Svc_GetSomeUsrDataFromUsrCod (struct UsrData *UsrDat,long CrsCod); static int Svc_CheckParamsNewAccount (char *NewNicknameWithArroba, // Input char NewNicknameWithoutArroba[Nck_MAX_BYTES_NICKNAME_FROM_FORM + 1], // Output char *NewEmail, // Input-output char *NewPlainPassword, // Input char *NewEncryptedPassword); // Output static void Svc_CopyListUsers (Rol_Role_t Role,struct swad__getUsersOutput *getUsersOut); static void Svc_CopyUsrData (struct swad__user *Usr,struct UsrData *UsrDat,bool UsrIDIsVisible); static void Svc_GetListGrpsInAttendanceEventFromDB (long AttCod,char **ListGroups); static void Svc_GetLstGrpsSel (const char *Groups); static int Svc_GetMyLanguage (void); static int Svc_SendMessageToUsr (long OriginalMsgCod,long SenderUsrCod,long ReplyUsrCod,long RecipientUsrCod,bool NotifyByEmail,const char *Subject,const char *Content); static int Svc_GetTstConfig (long CrsCod); static int Svc_GetNumTestQuestionsInCrs (long CrsCod); static int Svc_GetTstTags (long CrsCod,struct swad__getTestsOutput *getTestsOut); static int Svc_GetTstQuestions (long CrsCod,long BeginTime,struct swad__getTestsOutput *getTestsOut); static int Svc_GetTstAnswers (long CrsCod,long BeginTime,struct swad__getTestsOutput *getTestsOut); static int Svc_GetTstQuestionTags (long CrsCod,long BeginTime,struct swad__getTestsOutput *getTestsOut); static void Svc_ListDir (unsigned Level,const char *Path,const char *PathInTree); static bool Svc_WriteRowFileBrowser (unsigned Level,Brw_FileType_t FileType,const char *FileName); static void Svc_IndentXMLLine (unsigned Level); /*****************************************************************************/ /******* Function called when a web service if required by a plugin **********/ /*****************************************************************************/ void Svc_WebService (void) { struct soap *soap; if ((soap = soap_new ())) // Allocate and initialize runtime context { soap_serve (soap); soap_end (soap); // Clean up and remove deserialized data soap_free (soap); // Detach and free runtime context } } /*****************************************************************************/ /******* Function called to exit on error when executing web service *********/ /*****************************************************************************/ void Svc_Exit (const char *DetailErrorMessage) { int ReturnCode = (DetailErrorMessage ? soap_receiver_fault (Gbl.soap, "Error in swad web service", DetailErrorMessage) : 0); soap_end (Gbl.soap); // Clean up and remove deserialized data soap_free (Gbl.soap); // Detach and free runtime context exit (ReturnCode); } /*****************************************************************************/ /****** Check if the application key of the requester of a web service *******/ /****** is one of the application keys allowed in the plugins *******/ /*****************************************************************************/ static int Svc_GetPlgCodFromAppKey (const char *appKey) { MYSQL_RES *mysql_res; MYSQL_ROW row; Gbl.WebService.PlgCod = -1L; /***** Get number of plugins with a IP address *****/ if (DB_QuerySELECT (&mysql_res,"can not check application key", "SELECT PlgCod FROM plugins WHERE AppKey='%s'", appKey)) // Session found in table of sessions { row = mysql_fetch_row (mysql_res); Gbl.WebService.PlgCod = Str_ConvertStrCodToLongCod (row[0]); } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); if (Gbl.WebService.PlgCod < 0) return soap_sender_fault (Gbl.soap, "Unknown application key", "Unknown application"); return SOAP_OK; } /*****************************************************************************/ /****** Get the name of a web service function given the function code *******/ /*****************************************************************************/ const char *Svc_GetFunctionNameFromFunCod (long FunCod) { if (FunCod < 0 || FunCod > Svc_NUM_FUNCTIONS) return Svc_Functions[0]; return Svc_Functions[FunCod]; } /*****************************************************************************/ /****** Check if a session identifier is valid and exists in database ********/ /*****************************************************************************/ static int Svc_CheckIdSession (const char *IdSession) { const char *Ptr; unsigned i; /***** Check if pointer is NULL *****/ if (IdSession == NULL) return soap_sender_fault (Gbl.soap, "Bad session identifier", "Session identifier is a null pointer"); /***** Check length of session identifier *****/ if (strlen (IdSession) != Cns_BYTES_SESSION_ID) return soap_sender_fault (Gbl.soap, "Bad session identifier", "The length of the session identifier is wrong"); /***** Check if session identifier is in base64url *****/ for (Ptr = IdSession; *Ptr; Ptr++) { for (i = 0; i < 64; i++) // Check if this character is one of the allowed characters if (*Ptr == Str_BIN_TO_BASE64URL[i]) break; if (i == 64) // Character not found return soap_sender_fault (Gbl.soap, "Bad session identifier", "The session identifier must contain only base64url characters"); } /***** Query if session identifier already exists in database *****/ if (DB_QueryCOUNT ("can not get session data", "SELECT COUNT(*) FROM sessions" " WHERE SessionId='%s'", IdSession) != 1) return soap_receiver_fault (Gbl.soap, "Bad session identifier", "Session identifier does not exist in database"); return SOAP_OK; } /*****************************************************************************/ /************** Check if a web service key exists in database ****************/ /*****************************************************************************/ static int Svc_CheckWSKey (char WSKey[Svc_BYTES_WS_KEY + 1]) { MYSQL_RES *mysql_res; MYSQL_ROW row; /***** Set default user's code *****/ Gbl.Usrs.Me.UsrDat.UsrCod = -1L; Gbl.Usrs.Me.Logged = false; Gbl.WebService.PlgCod = -1L; /***** Check that key does not exist in database *****/ if (DB_QuerySELECT (&mysql_res,"can not get existence of key", "SELECT UsrCod,PlgCod FROM ws_keys WHERE WSKey='%s'", WSKey)) // Session found in table of sessions { row = mysql_fetch_row (mysql_res); Gbl.Usrs.Me.UsrDat.UsrCod = Str_ConvertStrCodToLongCod (row[0]); Gbl.Usrs.Me.Logged = true; Gbl.WebService.PlgCod = Str_ConvertStrCodToLongCod (row[1]); } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); return SOAP_OK; } /*****************************************************************************/ /** Check if a course code and a group code are valid and exist in database **/ /*****************************************************************************/ static int Svc_CheckCourseAndGroupCodes (long CrsCod,long GrpCod) { /***** Check if course code is correct *****/ if (CrsCod <= 0) return soap_sender_fault (Gbl.soap, "Bad course code", "Course code must be a integer greater than 0"); /***** Query if course code already exists in database *****/ if (DB_QueryCOUNT ("can not get course", "SELECT COUNT(*) FROM courses" " WHERE CrsCod=%ld", CrsCod) != 1) return soap_sender_fault (Gbl.soap, "Bad course code", "Course code does not exist in database"); /***** Course code exists in database, so check if group code exists in database and belongs to course *****/ if (GrpCod > 0) // <=0 means "the whole course" { /***** Query if group code already exists in database *****/ if (DB_QueryCOUNT ("can not get group", "SELECT COUNT(*) FROM crs_grp_types,crs_grp" " WHERE crs_grp_types.CrsCod=%ld" " AND crs_grp_types.GrpTypCod=crs_grp.GrpTypCod" " AND crs_grp.GrpCod=%ld", CrsCod,GrpCod) != 1) return soap_sender_fault (Gbl.soap, "Bad group code", "Group code does not exist in database or it's not a group of the specified course"); } return SOAP_OK; } /*****************************************************************************/ /***** Generate a key used in subsequents calls to other web services ********/ /*****************************************************************************/ static int Svc_GenerateNewWSKey (long UsrCod, char WSKey[Svc_BYTES_WS_KEY + 1]) { int ReturnCode; /***** Remove expired web service keys *****/ if ((ReturnCode = Svc_RemoveOldWSKeys ()) != SOAP_OK) return ReturnCode; /***** Create a unique name for the key *****/ Str_Copy (WSKey,Gbl.UniqueNameEncrypted, Svc_BYTES_WS_KEY); /***** Check that key does not exist in database *****/ if (DB_QueryCOUNT ("can not get existence of key", "SELECT COUNT(*) FROM ws_keys" " WHERE WSKey='%s'", WSKey)) return soap_receiver_fault (Gbl.soap, "Error when generating key", "Generated key already existed in database"); /***** Insert key into database *****/ DB_QueryINSERT ("can not insert new key", "INSERT INTO ws_keys" " (WSKey,UsrCod,PlgCod,LastTime)" " VALUES" " ('%s',%ld,%ld,NOW())", WSKey,UsrCod,Gbl.WebService.PlgCod); return SOAP_OK; } /*****************************************************************************/ /************************ Remove old web service keys ************************/ /*****************************************************************************/ static int Svc_RemoveOldWSKeys (void) { char Query[512]; /***** Remove expired sessions *****/ /* A session expire when last click (LastTime) is too old, or when there was at least one refresh (navigator supports AJAX) and last refresh is too old (browser probably was closed) */ sprintf (Query,"DELETE LOW_PRIORITY FROM ws_keys" " WHERE LastTimeUsrCod does not exist ini database static bool Svc_GetSomeUsrDataFromUsrCod (struct UsrData *UsrDat,long CrsCod) { MYSQL_RES *mysql_res; MYSQL_ROW row; /***** Check if user's code is valid *****/ if (UsrDat->UsrCod <= 0) return false; /***** Get some user's data *****/ if (DB_QuerySELECT (&mysql_res,"can not get user's data", "SELECT Surname1,Surname2,FirstName,Photo,DATE_FORMAT(Birthday,'%%Y%%m%%d')" " FROM usr_data WHERE UsrCod=%ld", UsrDat->UsrCod) != 1) return false; /* Read some user's data */ row = mysql_fetch_row (mysql_res); /* Get user's name */ Str_Copy (UsrDat->Surname1,row[0], Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME); Str_Copy (UsrDat->Surname2,row[1], Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME); Str_Copy (UsrDat->FirstName,row[2], Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME); /* Get user's photo */ Str_Copy (UsrDat->Photo,row[3], Cry_BYTES_ENCRYPTED_STR_SHA256_BASE64); /* Get user's brithday */ Dat_GetDateFromYYYYMMDD (&(UsrDat->Birthday),row[4]); /* Free structure that stores the query result */ DB_FreeMySQLResult (&mysql_res); /***** Get list of user's IDs *****/ ID_GetListIDsFromUsrCod (UsrDat); /***** Get user's nickname *****/ Nck_GetNicknameFromUsrCod (UsrDat->UsrCod,UsrDat->Nickname); /***** Get user's role *****/ /* Query database */ if (CrsCod > 0) { /* Get the role in the given course */ if (DB_QuerySELECT (&mysql_res,"can not get user's role", "SELECT Role FROM crs_usr" " WHERE CrsCod=%ld AND UsrCod=%ld", CrsCod,UsrDat->UsrCod)) // User belongs to course { row = mysql_fetch_row (mysql_res); if (row[0]) { if (sscanf (row[0],"%u",&UsrDat->Roles.InCurrentCrs.Role) == 1) UsrDat->Roles.InCurrentCrs.Valid = true; else { UsrDat->Roles.InCurrentCrs.Role = Rol_UNK; UsrDat->Roles.InCurrentCrs.Valid = false; } } else // Impossible { UsrDat->Roles.InCurrentCrs.Role = Rol_UNK; UsrDat->Roles.InCurrentCrs.Valid = false; } } else // User does not belong to course { UsrDat->Roles.InCurrentCrs.Role = Rol_UNK; UsrDat->Roles.InCurrentCrs.Valid = true; } } else { /* Get the maximum role in any course */ if (DB_QuerySELECT (&mysql_res,"can not get user's role", "SELECT MAX(Role)" " FROM crs_usr WHERE UsrCod=%ld", UsrDat->UsrCod) == 1) { row = mysql_fetch_row (mysql_res); if (row[0]) { if (sscanf (row[0],"%u",&UsrDat->Roles.InCurrentCrs.Role) == 1) UsrDat->Roles.InCurrentCrs.Valid = true; else { UsrDat->Roles.InCurrentCrs.Role = Rol_UNK; UsrDat->Roles.InCurrentCrs.Valid = false; } } else // MAX(Role) == NULL if user does not belong to any course { UsrDat->Roles.InCurrentCrs.Role = Rol_UNK; UsrDat->Roles.InCurrentCrs.Valid = true; } } else // Impossible { UsrDat->Roles.InCurrentCrs.Role = Rol_UNK; UsrDat->Roles.InCurrentCrs.Valid = false; } } /* Free structure that stores the query result */ DB_FreeMySQLResult (&mysql_res); return true; } /*****************************************************************************/ /**************************** Get info of my marks ***************************/ /*****************************************************************************/ #define Svc_CHECK_NEW_ACCOUNT_OK 0 #define Svc_CHECK_NEW_ACCOUNT_NICKNAME_NOT_VALID -1 #define Svc_CHECK_NEW_ACCOUNT_NICKNAME_REGISTERED_BY_ANOTHER_USER -2 #define Svc_CHECK_NEW_ACCOUNT_EMAIL_NOT_VALID -3 #define Svc_CHECK_NEW_ACCOUNT_EMAIL_REGISTERED_BY_ANOTHER_USER -4 #define Svc_CHECK_NEW_ACCOUNT_PASSWORD_NOT_VALID -5 int swad__createAccount (struct soap *soap, char *userNickname,char *userEmail,char *userPassword,char *appKey, // input struct swad__createAccountOutput *createAccountOut) // output { char NewNicknameWithoutArroba[Nck_MAX_BYTES_NICKNAME_FROM_FORM + 1]; char NewEncryptedPassword[Pwd_BYTES_ENCRYPTED_PASSWORD + 1]; int Result; int ReturnCode; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_createAccount; /***** Allocate space for strings *****/ createAccountOut->wsKey = (char *) soap_malloc (Gbl.soap,Svc_BYTES_WS_KEY + 1); /***** Default values returned on error *****/ createAccountOut->userCode = 0; // Undefined error createAccountOut->wsKey[0] = '\0'; /***** Get plugin code *****/ if ((ReturnCode = Svc_GetPlgCodFromAppKey ((const char *) appKey)) != SOAP_OK) return ReturnCode; /***** Check parameters used to create the new account *****/ Result = Svc_CheckParamsNewAccount (userNickname, // Input NewNicknameWithoutArroba,// Output userEmail, // Input-output userPassword, // Input NewEncryptedPassword); // Output if (Result < 0) { createAccountOut->userCode = Result; return SOAP_OK; } /***** User's has no ID *****/ Gbl.Usrs.Me.UsrDat.IDs.Num = 0; Gbl.Usrs.Me.UsrDat.IDs.List = NULL; /***** Set password to the password typed by the user *****/ Str_Copy (Gbl.Usrs.Me.UsrDat.Password,NewEncryptedPassword, Pwd_BYTES_ENCRYPTED_PASSWORD); /***** User does not exist in the platform, so create him/her! *****/ Acc_CreateNewUsr (&Gbl.Usrs.Me.UsrDat, true); // I am creating my own account /***** Save nickname *****/ Nck_UpdateNickInDB (Gbl.Usrs.Me.UsrDat.UsrCod,NewNicknameWithoutArroba); Str_Copy (Gbl.Usrs.Me.UsrDat.Nickname,NewNicknameWithoutArroba, Nck_MAX_BYTES_NICKNAME_WITHOUT_ARROBA); /***** Save email *****/ if (Mai_UpdateEmailInDB (&Gbl.Usrs.Me.UsrDat,userEmail)) { /* Email updated sucessfully */ Str_Copy (Gbl.Usrs.Me.UsrDat.Email,userEmail, Cns_MAX_BYTES_EMAIL_ADDRESS); Gbl.Usrs.Me.UsrDat.EmailConfirmed = false; } /***** Copy new user's code *****/ createAccountOut->userCode = Gbl.Usrs.Me.UsrDat.UsrCod; /***** Generate a key used in subsequents calls to other web services *****/ return Svc_GenerateNewWSKey ((long) createAccountOut->userCode, createAccountOut->wsKey); } /*****************************************************************************/ /************* Get parameters for the creation of a new account **************/ /*****************************************************************************/ // Return false on error //char *userNickname,char *userEmail,char *userID,char *userPassword static int Svc_CheckParamsNewAccount (char *NewNicknameWithArroba, // Input char NewNicknameWithoutArroba[Nck_MAX_BYTES_NICKNAME_FROM_FORM + 1], // Output char *NewEmail, // Input-output char *NewPlainPassword, // Input char *NewEncryptedPassword) // Output { /***** Step 1/3: Check new nickname *****/ /* Make a copy without possible starting arrobas */ Str_Copy (NewNicknameWithoutArroba,NewNicknameWithArroba, Nck_MAX_BYTES_NICKNAME_FROM_FORM); if (Nck_CheckIfNickWithArrobaIsValid (NewNicknameWithArroba)) // If new nickname is valid { /***** Remove arrobas at the beginning *****/ Str_RemoveLeadingArrobas (NewNicknameWithoutArroba); /***** Check if the new nickname matches any of the nicknames of other users *****/ if (DB_QueryCOUNT ("can not check if nickname already existed", "SELECT COUNT(*) FROM usr_nicknames" " WHERE Nickname='%s'", NewNicknameWithoutArroba)) // A nickname of another user is the same that this nickname return Svc_CHECK_NEW_ACCOUNT_NICKNAME_REGISTERED_BY_ANOTHER_USER; } else // New nickname is not valid return Svc_CHECK_NEW_ACCOUNT_NICKNAME_NOT_VALID; /***** Step 2/3: Check new email *****/ if (Mai_CheckIfEmailIsValid (NewEmail)) // New email is valid { /***** Check if the new email matches any of the confirmed emails of other users *****/ if (DB_QueryCOUNT ("can not check if email already existed", "SELECT COUNT(*) FROM usr_emails" " WHERE E_mail='%s' AND Confirmed='Y'", NewEmail)) // An email of another user is the same that my email return Svc_CHECK_NEW_ACCOUNT_EMAIL_REGISTERED_BY_ANOTHER_USER; } else // New email is not valid return Svc_CHECK_NEW_ACCOUNT_EMAIL_NOT_VALID; /***** Step 3/3: Check new password *****/ Cry_EncryptSHA512Base64 (NewPlainPassword,NewEncryptedPassword); if (!Pwd_SlowCheckIfPasswordIsGood (NewPlainPassword,NewEncryptedPassword,-1L)) // New password is good? return Svc_CHECK_NEW_ACCOUNT_PASSWORD_NOT_VALID; return Svc_CHECK_NEW_ACCOUNT_OK; } /*****************************************************************************/ /****************** Login user by user, password and key *********************/ /*****************************************************************************/ int swad__loginByUserPasswordKey (struct soap *soap, char *userID,char *userPassword,char *appKey, // input struct swad__loginByUserPasswordKeyOutput *loginByUserPasswordKeyOut) // output { char UsrIDNickOrEmail[Cns_MAX_BYTES_EMAIL_ADDRESS + 1]; int ReturnCode; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRows; char PhotoURL[Cns_MAX_BYTES_WWW + 1]; bool UsrFound; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_loginByUserPasswordKey; /***** Allocate space for strings *****/ loginByUserPasswordKeyOut->wsKey = (char *) soap_malloc (Gbl.soap,Svc_BYTES_WS_KEY + 1); loginByUserPasswordKeyOut->userNickname = (char *) soap_malloc (Gbl.soap,Nck_MAX_BYTES_NICKNAME_WITHOUT_ARROBA + 1); loginByUserPasswordKeyOut->userID = (char *) soap_malloc (Gbl.soap,ID_MAX_BYTES_USR_ID + 1); loginByUserPasswordKeyOut->userFirstname = (char *) soap_malloc (Gbl.soap,Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME + 1); loginByUserPasswordKeyOut->userSurname1 = (char *) soap_malloc (Gbl.soap,Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME + 1); loginByUserPasswordKeyOut->userSurname2 = (char *) soap_malloc (Gbl.soap,Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME + 1); loginByUserPasswordKeyOut->userPhoto = (char *) soap_malloc (Gbl.soap,Cns_MAX_BYTES_WWW + 1); loginByUserPasswordKeyOut->userBirthday = (char *) soap_malloc (Gbl.soap,Dat_LENGTH_YYYYMMDD + 1); /***** Default values returned on error *****/ loginByUserPasswordKeyOut->userCode = -1; loginByUserPasswordKeyOut->wsKey[0] = '\0'; loginByUserPasswordKeyOut->userNickname[0] = '\0'; loginByUserPasswordKeyOut->userID[0] = '\0'; loginByUserPasswordKeyOut->userFirstname[0] = '\0'; loginByUserPasswordKeyOut->userSurname1[0] = '\0'; loginByUserPasswordKeyOut->userSurname2[0] = '\0'; loginByUserPasswordKeyOut->userPhoto[0] = '\0'; loginByUserPasswordKeyOut->userBirthday[0] = '\0'; loginByUserPasswordKeyOut->userRole = 0; // unknown /***** Get plugin code *****/ if ((ReturnCode = Svc_GetPlgCodFromAppKey ((const char *) appKey)) != SOAP_OK) return ReturnCode; /***** Check if user's email, @nickname or ID are valid *****/ Str_Copy (UsrIDNickOrEmail,userID, Cns_MAX_BYTES_EMAIL_ADDRESS); if (Nck_CheckIfNickWithArrobaIsValid (UsrIDNickOrEmail)) // 1: It's a nickname { Str_RemoveLeadingArrobas (UsrIDNickOrEmail); /* User has typed a nickname */ NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get user's data", "SELECT usr_nicknames.UsrCod" " FROM usr_nicknames,usr_data" " WHERE usr_nicknames.Nickname='%s'" " AND usr_nicknames.UsrCod=usr_data.UsrCod" " AND usr_data.Password='%s'", UsrIDNickOrEmail,userPassword); } else if (Mai_CheckIfEmailIsValid (Gbl.Usrs.Me.UsrIdLogin)) // 2: It's an email { /* User has typed an email */ // TODO: Get only if email confirmed? NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get user's data", "SELECT usr_emails.UsrCod" " FROM usr_emails,usr_data" " WHERE usr_emails.E_mail='%s'" " AND usr_emails.UsrCod=usr_data.UsrCod" " AND usr_data.Password='%s'", UsrIDNickOrEmail,userPassword); } else // 3: It's not a nickname nor email { // Users' IDs are always stored internally in capitals and without leading zeros Str_RemoveLeadingZeros (UsrIDNickOrEmail); Str_ConvertToUpperText (UsrIDNickOrEmail); if (ID_CheckIfUsrIDIsValid (UsrIDNickOrEmail)) { /* User has typed a valid user's ID (existing or not) */ // TODO: Get only if ID confirmed? NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get user's data", "SELECT usr_IDs.UsrCod" " FROM usr_IDs,usr_data" " WHERE usr_IDs.UsrID='%s'" " AND usr_IDs.UsrCod=usr_data.UsrCod" " AND usr_data.Password='%s'", UsrIDNickOrEmail,userPassword); } else // String is not a valid user's nickname, email or ID return soap_receiver_fault (Gbl.soap, "Bad log in", "User's ID or nickname don't exist or password is wrong"); } /***** Get user's data from database *****/ if (NumRows == 1) // User found in table of users' data { row = mysql_fetch_row (mysql_res); /***** Get user code (row[0]) *****/ Gbl.Usrs.Me.UsrDat.UsrCod = Str_ConvertStrCodToLongCod (row[0]); /***** Get user's data *****/ UsrFound = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,-1L); // Get some user's data from database } else UsrFound = false; /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); if (UsrFound) { Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; loginByUserPasswordKeyOut->userCode = (int) Gbl.Usrs.Me.UsrDat.UsrCod; Str_Copy (loginByUserPasswordKeyOut->userNickname, Gbl.Usrs.Me.UsrDat.Nickname, Nck_MAX_BYTES_NICKNAME_WITHOUT_ARROBA); if (Gbl.Usrs.Me.UsrDat.IDs.Num) Str_Copy (loginByUserPasswordKeyOut->userID, Gbl.Usrs.Me.UsrDat.IDs.List[0].ID, // TODO: What user's ID? ID_MAX_BYTES_USR_ID); Str_Copy (loginByUserPasswordKeyOut->userSurname1, Gbl.Usrs.Me.UsrDat.Surname1, Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME); Str_Copy (loginByUserPasswordKeyOut->userSurname2, Gbl.Usrs.Me.UsrDat.Surname2, Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME); Str_Copy (loginByUserPasswordKeyOut->userFirstname, Gbl.Usrs.Me.UsrDat.FirstName, Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME); Pho_BuildLinkToPhoto (&Gbl.Usrs.Me.UsrDat,PhotoURL); Str_Copy (loginByUserPasswordKeyOut->userPhoto,PhotoURL, Cns_MAX_BYTES_WWW); Str_Copy (loginByUserPasswordKeyOut->userBirthday, Gbl.Usrs.Me.UsrDat.Birthday.YYYYMMDD, Dat_LENGTH_YYYYMMDD); loginByUserPasswordKeyOut->userRole = Svc_RolRole_to_SvcRole[Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role]; /***** Generate a key used in subsequents calls to other web services *****/ return Svc_GenerateNewWSKey ((long) loginByUserPasswordKeyOut->userCode, loginByUserPasswordKeyOut->wsKey); } else { loginByUserPasswordKeyOut->userCode = -1; loginByUserPasswordKeyOut->userID = NULL; loginByUserPasswordKeyOut->userSurname1 = NULL; loginByUserPasswordKeyOut->userSurname2 = NULL; loginByUserPasswordKeyOut->userFirstname = NULL; loginByUserPasswordKeyOut->userPhoto = NULL; loginByUserPasswordKeyOut->userRole = 0; return soap_receiver_fault (Gbl.soap, "Bad log in", "User's ID or nickname don't exist or password is wrong"); } } /*****************************************************************************/ /************************** Login user by session ****************************/ /*****************************************************************************/ int swad__loginBySessionKey (struct soap *soap, char *sessionID,char *appKey, // input struct swad__loginBySessionKeyOutput *loginBySessionKeyOut) // output { int ReturnCode; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRows; char PhotoURL[Cns_MAX_BYTES_WWW + 1]; bool UsrFound; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_loginBySessionKey; /***** Allocate space for strings *****/ loginBySessionKeyOut->wsKey = (char *) soap_malloc (Gbl.soap,Svc_BYTES_WS_KEY + 1); loginBySessionKeyOut->userNickname = (char *) soap_malloc (Gbl.soap,Nck_MAX_BYTES_NICKNAME_WITHOUT_ARROBA + 1); loginBySessionKeyOut->userID = (char *) soap_malloc (Gbl.soap,ID_MAX_BYTES_USR_ID + 1); loginBySessionKeyOut->userFirstname = (char *) soap_malloc (Gbl.soap,Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME + 1); loginBySessionKeyOut->userSurname1 = (char *) soap_malloc (Gbl.soap,Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME + 1); loginBySessionKeyOut->userSurname2 = (char *) soap_malloc (Gbl.soap,Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME + 1); loginBySessionKeyOut->userPhoto = (char *) soap_malloc (Gbl.soap,Cns_MAX_BYTES_WWW + 1); loginBySessionKeyOut->userBirthday = (char *) soap_malloc (Gbl.soap,Dat_LENGTH_YYYYMMDD + 1); loginBySessionKeyOut->degreeName = (char *) soap_malloc (Gbl.soap,Hie_MAX_BYTES_FULL_NAME + 1); loginBySessionKeyOut->courseName = (char *) soap_malloc (Gbl.soap,Hie_MAX_BYTES_FULL_NAME + 1); /***** Default values returned on error *****/ loginBySessionKeyOut->userCode = -1; loginBySessionKeyOut->degreeCode = -1; loginBySessionKeyOut->courseCode = -1; loginBySessionKeyOut->wsKey[0] = '\0'; loginBySessionKeyOut->userNickname[0] = '\0'; loginBySessionKeyOut->userID[0] = '\0'; loginBySessionKeyOut->userFirstname[0] = '\0'; loginBySessionKeyOut->userSurname1[0] = '\0'; loginBySessionKeyOut->userSurname2[0] = '\0'; loginBySessionKeyOut->userPhoto[0] = '\0'; loginBySessionKeyOut->userBirthday[0] = '\0'; loginBySessionKeyOut->userRole = Rol_UNK; loginBySessionKeyOut->degreeName[0] = '\0'; loginBySessionKeyOut->courseName[0] = '\0'; /***** Get plugin code *****/ if ((ReturnCode = Svc_GetPlgCodFromAppKey ((const char *) appKey)) != SOAP_OK) return ReturnCode; /***** Check length of session identifier *****/ if (sessionID == NULL) return soap_sender_fault (Gbl.soap, "SessionID is null", "Login by session"); /***** Check session identifier coming from an external plugin *****/ if ((ReturnCode = Svc_CheckIdSession (sessionID)) != SOAP_OK) return ReturnCode; // Now, we know that sessionID is a valid session identifier /***** Query data of the session from database *****/ NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get session data", "SELECT UsrCod,DegCod,CrsCod FROM sessions" " WHERE SessionId='%s'", sessionID); if (NumRows == 1) // Session found in table of sessions { row = mysql_fetch_row (mysql_res); /***** Get course (row[2]) *****/ Gbl.Hierarchy.Crs.CrsCod = Str_ConvertStrCodToLongCod (row[2]); Crs_GetDataOfCourseByCod (&Gbl.Hierarchy.Crs); loginBySessionKeyOut->courseCode = (int) Gbl.Hierarchy.Crs.CrsCod; Str_Copy (loginBySessionKeyOut->courseName,Gbl.Hierarchy.Crs.FullName, Hie_MAX_BYTES_FULL_NAME); /***** Get user code (row[0]) *****/ Gbl.Usrs.Me.UsrDat.UsrCod = Str_ConvertStrCodToLongCod (row[0]); UsrFound = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.Hierarchy.Crs.CrsCod); // Get some user's data from database /***** Get degree (row[1]) *****/ Gbl.Hierarchy.Deg.DegCod = Str_ConvertStrCodToLongCod (row[1]); Deg_GetDataOfDegreeByCod (&Gbl.Hierarchy.Deg); loginBySessionKeyOut->degreeCode = (int) Gbl.Hierarchy.Deg.DegCod; Str_Copy (loginBySessionKeyOut->degreeName,Gbl.Hierarchy.Deg.FullName, Hie_MAX_BYTES_FULL_NAME); } else UsrFound = false; /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); /***** Get degree of current course *****/ if ((ReturnCode = Svc_GetCurrentDegCodFromCurrentCrsCod ()) != SOAP_OK) return ReturnCode; if (UsrFound) { Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; loginBySessionKeyOut->userCode = (int) Gbl.Usrs.Me.UsrDat.UsrCod; Str_Copy (loginBySessionKeyOut->userNickname,Gbl.Usrs.Me.UsrDat.Nickname, Nck_MAX_BYTES_NICKNAME_WITHOUT_ARROBA); if (Gbl.Usrs.Me.UsrDat.IDs.Num) Str_Copy (loginBySessionKeyOut->userID, Gbl.Usrs.Me.UsrDat.IDs.List[0].ID, // TODO: What user's ID? ID_MAX_BYTES_USR_ID); Str_Copy (loginBySessionKeyOut->userSurname1, Gbl.Usrs.Me.UsrDat.Surname1, Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME); Str_Copy (loginBySessionKeyOut->userSurname2, Gbl.Usrs.Me.UsrDat.Surname2, Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME); Str_Copy (loginBySessionKeyOut->userFirstname, Gbl.Usrs.Me.UsrDat.FirstName, Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME); Pho_BuildLinkToPhoto (&Gbl.Usrs.Me.UsrDat,PhotoURL); Str_Copy (loginBySessionKeyOut->userPhoto,PhotoURL, Cns_MAX_BYTES_WWW); Str_Copy (loginBySessionKeyOut->userBirthday, Gbl.Usrs.Me.UsrDat.Birthday.YYYYMMDD, Dat_LENGTH_YYYYMMDD); loginBySessionKeyOut->userRole = Svc_RolRole_to_SvcRole[Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role]; /***** Generate a key used in subsequents calls to other web services *****/ return Svc_GenerateNewWSKey ((long) loginBySessionKeyOut->userCode, loginBySessionKeyOut->wsKey); } else return soap_receiver_fault (Gbl.soap, "Bad session identifier", "Session identifier does not exist in database"); } /*****************************************************************************/ /*********************** Send a new password by email ************************/ /*****************************************************************************/ int swad__getNewPassword (struct soap *soap, char *userID,char *appKey, // input struct swad__getNewPasswordOutput *getNewPasswordOut) // output { int ReturnCode; char UsrIDNickOrEmail[Cns_MAX_BYTES_EMAIL_ADDRESS + 1]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRows; char NewRandomPlainPassword[Pwd_MAX_BYTES_PLAIN_PASSWORD + 1]; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_getNewPassword; /***** Default values returned on error *****/ getNewPasswordOut->success = 0; // error /***** Get plugin code *****/ if ((ReturnCode = Svc_GetPlgCodFromAppKey ((const char *) appKey)) != SOAP_OK) return ReturnCode; /***** Check if user's email, @nickname or ID are valid *****/ Str_Copy (UsrIDNickOrEmail,userID, Cns_MAX_BYTES_EMAIL_ADDRESS); if (Nck_CheckIfNickWithArrobaIsValid (UsrIDNickOrEmail)) // 1: It's a nickname { Str_RemoveLeadingArrobas (UsrIDNickOrEmail); /* User has typed a nickname */ NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get user's data", "SELECT UsrCod FROM usr_nicknames" " WHERE Nickname='%s'", UsrIDNickOrEmail); } else if (Mai_CheckIfEmailIsValid (Gbl.Usrs.Me.UsrIdLogin)) // 2: It's an email { /* User has typed an email */ // TODO: Get only if email confirmed? NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get user's data", "SELECT UsrCod FROM usr_emails" " WHERE E_mail='%s'", UsrIDNickOrEmail); } else // 3: It's not a nickname nor email { // Users' IDs are always stored internally in capitals and without leading zeros Str_RemoveLeadingZeros (UsrIDNickOrEmail); Str_ConvertToUpperText (UsrIDNickOrEmail); if (ID_CheckIfUsrIDIsValid (UsrIDNickOrEmail)) { /* User has typed a valid user's ID (existing or not) */ // TODO: Get only if ID confirmed? NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get user's data", "SELECT UsrCod FROM usr_IDs" " WHERE UsrID='%s'", UsrIDNickOrEmail); } else // String is not a valid user's nickname, email or ID return soap_receiver_fault (Gbl.soap, "Bad log in", "User's email, nickname or ID don't exist"); } /***** Get user's data from database *****/ if (NumRows == 1) // One unique user found in table of users' data { row = mysql_fetch_row (mysql_res); /***** Get user code (row[0]) *****/ Gbl.Usrs.Me.UsrDat.UsrCod = Str_ConvertStrCodToLongCod (row[0]); Usr_GetUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Usr_DONT_GET_PREFS); // Get my data if (Gbl.Usrs.Me.UsrDat.Email[0]) if (Pwd_SendNewPasswordByEmail (NewRandomPlainPassword) == 0) // Message sent successfully { Pwd_SetMyPendingPassword (NewRandomPlainPassword); getNewPasswordOut->success = 1; } } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); return SOAP_OK; } /*****************************************************************************/ /************************ Return courses of a user ***************************/ /*****************************************************************************/ int swad__getCourses (struct soap *soap, char *wsKey, // input struct swad__getCoursesOutput *getCoursesOut) // output { int ReturnCode; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow; unsigned NumRows; Rol_Role_t Role; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_getCourses; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,-1L)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Query my courses from database *****/ NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get user's courses", "SELECT courses.CrsCod," "courses.ShortName," "courses.FullName," "crs_usr.Role" " FROM crs_usr,courses" " WHERE crs_usr.UsrCod=%ld" " AND crs_usr.CrsCod=courses.CrsCod" " ORDER BY courses.FullName", Gbl.Usrs.Me.UsrDat.UsrCod); getCoursesOut->numCourses = (int) NumRows; getCoursesOut->coursesArray.__size = (int) NumRows; if (NumRows == 0) getCoursesOut->coursesArray.__ptr = NULL; else // Courses found { getCoursesOut->coursesArray.__ptr = soap_malloc (Gbl.soap,(getCoursesOut->coursesArray.__size) * sizeof (*(getCoursesOut->coursesArray.__ptr))); for (NumRow = 0; NumRow < NumRows; NumRow++) { /* Get next course */ row = mysql_fetch_row (mysql_res); /* Get course code (row[0]) */ getCoursesOut->coursesArray.__ptr[NumRow].courseCode = (int) Str_ConvertStrCodToLongCod (row[0]); /* Get course short name (row[1]) */ getCoursesOut->coursesArray.__ptr[NumRow].courseShortName = (char *) soap_malloc (Gbl.soap,Hie_MAX_BYTES_SHRT_NAME + 1); Str_Copy (getCoursesOut->coursesArray.__ptr[NumRow].courseShortName,row[1], Hie_MAX_BYTES_SHRT_NAME); /* Get course full name (row[2]) */ getCoursesOut->coursesArray.__ptr[NumRow].courseFullName = (char *) soap_malloc (Gbl.soap,Hie_MAX_BYTES_FULL_NAME + 1); Str_Copy (getCoursesOut->coursesArray.__ptr[NumRow].courseFullName,row[2], Hie_MAX_BYTES_FULL_NAME); /* Get role (row[3]) */ if (sscanf (row[3],"%u",&Role) != 1) // Role in this course Role = Rol_UNK; getCoursesOut->coursesArray.__ptr[NumRow].userRole = Svc_RolRole_to_SvcRole[Role]; } } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); return SOAP_OK; } /*****************************************************************************/ /************************ Return course information **************************/ /*****************************************************************************/ // TODO: Not completely implemented int swad__getCourseInfo (struct soap *soap, char *wsKey,int courseCode,char *infoType, // input struct swad__getCourseInfoOutput *getCourseInfo) // output { int ReturnCode; Inf_InfoType_t InfoType; size_t Length; Inf_InfoSrc_t InfoSrc; bool MustBeRead; int Result = SOAP_OK; const char *NamesInWSForInfoType[Inf_NUM_INFO_TYPES] = { "introduction", // Inf_INTRODUCTION "guide", // Inf_TEACHING_GUIDE "lectures", // Inf_LECTURES "practicals", // Inf_PRACTICALS "bibliography", // Inf_BIBLIOGRAPHY "FAQ", // Inf_FAQ "links", // Inf_LINKS "assessment", // Inf_ASSESSMENT }; const char *NamesInWSForInfoSrc[Inf_NUM_INFO_SOURCES] = { "none", // Inf_INFO_SRC_NONE "editor", // Inf_INFO_SRC_EDITOR "plainText", // Inf_INFO_SRC_PLAIN_TEXT "richText", // Inf_INFO_SRC_RICH_TEXT "page", // Inf_INFO_SRC_PAGE "URL", // Inf_INFO_SRC_URL }; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_getCourseInfo; Gbl.Hierarchy.Crs.CrsCod = (long) courseCode; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /***** Check course and group codes *****/ if ((ReturnCode = Svc_CheckCourseAndGroupCodes (Gbl.Hierarchy.Crs.CrsCod,-1L)) != SOAP_OK) return ReturnCode; /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.Hierarchy.Crs.CrsCod)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Check if I am a student, non-editing teacher or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_STD && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_NET && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_TCH) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to course"); /***** Get info source *****/ for (InfoType = (Inf_InfoType_t) 0; InfoType < Inf_NUM_INFO_TYPES; InfoType++) if (!strcmp (infoType,NamesInWSForInfoType[InfoType])) break; if (InfoType == Inf_NUM_INFO_TYPES) // Not found! return soap_receiver_fault (Gbl.soap, "Bad info type", "Unknown requested info type"); Gbl.Crs.Info.Type = InfoType; Inf_GetAndCheckInfoSrcFromDB (Gbl.Hierarchy.Crs.CrsCod,Gbl.Crs.Info.Type,&InfoSrc,&MustBeRead); Length = strlen (NamesInWSForInfoSrc[InfoSrc]); getCourseInfo->infoSrc = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (getCourseInfo->infoSrc,NamesInWSForInfoSrc[InfoSrc], Length); /***** Set paths *****/ Hie_InitHierarchy (); /***** Get info text *****/ getCourseInfo->infoTxt = NULL; switch (InfoSrc) { case Inf_INFO_SRC_NONE: // No info available break; case Inf_INFO_SRC_EDITOR: // Internal editor (only for syllabus) switch (Gbl.Crs.Info.Type) { case Inf_LECTURES: // Syllabus (lectures) case Inf_PRACTICALS: // Syllabys (practicals) Result = Syl_WriteSyllabusIntoHTMLBuffer (&(getCourseInfo->infoTxt)); break; default: break; } break; case Inf_INFO_SRC_PLAIN_TEXT: // Plain text case Inf_INFO_SRC_RICH_TEXT: // Rich text (not yet available) Result = Inf_WritePlainTextIntoHTMLBuffer (&(getCourseInfo->infoTxt)); break; case Inf_INFO_SRC_PAGE: // Web page hosted in SWAD server Result = Inf_WritePageIntoHTMLBuffer (&(getCourseInfo->infoTxt)); break; case Inf_INFO_SRC_URL: // Link to a web page getCourseInfo->infoTxt = (char *) soap_malloc (Gbl.soap,Cns_MAX_BYTES_WWW + 1); Inf_WriteURLIntoTxtBuffer (getCourseInfo->infoTxt); break; } /***** Return empty text if pointer is null *****/ if (getCourseInfo->infoTxt == NULL) { getCourseInfo->infoTxt = (char *) soap_malloc (Gbl.soap,1); getCourseInfo->infoTxt[0] = '\0'; } return Result; } /*****************************************************************************/ /************* Get users in a course (and optionally in groups) **************/ /*****************************************************************************/ int swad__getUsers (struct soap *soap, char *wsKey,int courseCode,char *groups,int userRole, // input struct swad__getUsersOutput *getUsersOut) // output { int ReturnCode; Rol_Role_t Role; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_getUsers; Gbl.Hierarchy.Crs.CrsCod = (courseCode > 0) ? (long) courseCode : -1L; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /***** Check course *****/ if ((ReturnCode = Svc_CheckCourseAndGroupCodes (Gbl.Hierarchy.Crs.CrsCod,-1L)) != SOAP_OK) return ReturnCode; /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.Hierarchy.Crs.CrsCod)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Check if I am a student, non-editing teacher or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_STD && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_NET && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_TCH) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to course"); /***** Get degree of current course *****/ if ((ReturnCode = Svc_GetCurrentDegCodFromCurrentCrsCod ()) != SOAP_OK) // TODO: Is this necessary? return ReturnCode; /***** Check requested users' role *****/ if (userRole != Svc_ROLE_STUDENT && // Students userRole != Svc_ROLE_TEACHER) // Teachers return soap_sender_fault (Gbl.soap, "Bad requested users' type", "User roles allowed are 2 (students) or 3 (teachers)"); Role = Svc_SvcRole_to_RolRole[userRole]; /***** Create a list of groups selected *****/ Svc_GetLstGrpsSel (groups); if (Gbl.Crs.Grps.LstGrpsSel.NumGrps) /***** Get list of groups types and groups in current course *****/ Grp_GetListGrpTypesInThisCrs (Grp_ONLY_GROUP_TYPES_WITH_GROUPS); /***** Get list of users *****/ Usr_GetListUsrs (Hie_CRS,Role); Svc_CopyListUsers (Role,getUsersOut); Usr_FreeUsrsList (Role); if (Gbl.Crs.Grps.LstGrpsSel.NumGrps) { /***** Free list of groups types and groups in current course *****/ Grp_FreeListGrpTypesAndGrps (); /***** Free memory for list of selected groups *****/ Grp_FreeListCodSelectedGrps (); } return SOAP_OK; } int swad__findUsers (struct soap *soap, char *wsKey,int courseCode,char *filter,int userRole, // input struct swad__getUsersOutput *getUsersOut) // output { int ReturnCode; char SearchQuery[Sch_MAX_BYTES_SEARCH_QUERY + 1]; Rol_Role_t Role; bool FilterTooShort = false; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_findUsers; Gbl.Hierarchy.Crs.CrsCod = (courseCode > 0) ? (long) courseCode : -1L; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); if (Gbl.Hierarchy.Level == Hie_CRS) // Course selected /***** Check course *****/ if ((ReturnCode = Svc_CheckCourseAndGroupCodes (Gbl.Hierarchy.Crs.CrsCod,-1L)) != SOAP_OK) return ReturnCode; /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.Hierarchy.Crs.CrsCod)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; if (Gbl.Hierarchy.Level == Hie_CRS) // Course selected /***** Check if I am a student, non-editing teacher or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_STD && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_NET && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_TCH) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to course"); if (Gbl.Hierarchy.Level == Hie_CRS) { /***** Get degree of current course *****/ if ((ReturnCode = Svc_GetCurrentDegCodFromCurrentCrsCod ()) != SOAP_OK) // TODO: Is this necessary? return ReturnCode; } /***** Check requested users' role *****/ if (userRole < Svc_ROLE_UNKNOWN || userRole > Svc_ROLE_TEACHER) return soap_sender_fault (Gbl.soap, "Bad requested users' type", "User roles allowed are 0 (all), 1 (guests), 2 (students) or 3 (teachers)"); Role = Svc_SvcRole_to_RolRole[userRole]; /***** Query users beloging to course or group from database *****/ Str_Copy (Gbl.Search.Str,filter, Sch_MAX_BYTES_STRING_TO_FIND); if (Gbl.Search.Str[0]) // Search some users { Gbl.Scope.Current = (Gbl.Hierarchy.Level == Hie_CRS) ? Hie_CRS : Hie_SYS; if (Sch_BuildSearchQuery (SearchQuery, "CONCAT_WS(' ',FirstName,Surname1,Surname2)", NULL,NULL)) { /***** Create temporary table with candidate users *****/ // Search is faster (aproximately x2) using temporary tables Usr_CreateTmpTableAndSearchCandidateUsrs (SearchQuery); /***** Search for users *****/ Usr_SearchListUsrs (Role); Svc_CopyListUsers (Role,getUsersOut); Usr_FreeUsrsList (Role); /***** Drop temporary table with candidate users *****/ Usr_DropTmpTableWithCandidateUsrs (); } else FilterTooShort = true; } else FilterTooShort = true; /***** Return error in filter? *****/ if (FilterTooShort) { getUsersOut->numUsers = -1; // < 0 ==> filter too short getUsersOut->usersArray.__size = 0; getUsersOut->usersArray.__ptr = NULL; } return SOAP_OK; } /*****************************************************************************/ /***************************** Copy users from list **************************/ /*****************************************************************************/ static void Svc_CopyListUsers (Rol_Role_t Role,struct swad__getUsersOutput *getUsersOut) { unsigned NumUsrs; unsigned NumUsr; struct UsrData UsrDat; bool ICanSeeUsrID; /***** Initialize result *****/ getUsersOut->numUsers = 0; getUsersOut->usersArray.__size = 0; getUsersOut->usersArray.__ptr = NULL; NumUsrs = Gbl.Usrs.LstUsrs[Role].NumUsrs; if (NumUsrs) { getUsersOut->numUsers = (int) NumUsrs; getUsersOut->usersArray.__size = (int) NumUsrs; getUsersOut->usersArray.__ptr = soap_malloc (Gbl.soap,(getUsersOut->usersArray.__size) * sizeof (*(getUsersOut->usersArray.__ptr))); /***** Initialize structure with user's data *****/ Usr_UsrDataConstructor (&UsrDat); /***** List data of users *****/ for (NumUsr = 0; NumUsr < NumUsrs; NumUsr++) { /* Copy user's basic data from list */ Usr_CopyBasicUsrDataFromList (&UsrDat,&Gbl.Usrs.LstUsrs[Role].Lst[NumUsr]); /* Get list of user's IDs */ ID_GetListIDsFromUsrCod (&UsrDat); ICanSeeUsrID = ID_ICanSeeOtherUsrIDs (&UsrDat); /* Get nickname */ Nck_GetNicknameFromUsrCod (UsrDat.UsrCod,UsrDat.Nickname); /* Copy user's data into output structure */ Svc_CopyUsrData (&(getUsersOut->usersArray.__ptr[NumUsr]),&UsrDat,ICanSeeUsrID); } /***** Free memory used for user's data *****/ Usr_UsrDataDestructor (&UsrDat); } } /*****************************************************************************/ /********************** Return group types in a course ***********************/ /*****************************************************************************/ int swad__getGroupTypes (struct soap *soap, char *wsKey,int courseCode, // input struct swad__getGroupTypesOutput *getGroupTypesOut) // output { int ReturnCode; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow; unsigned NumRows; long OpenTime; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_getGroupTypes; Gbl.Hierarchy.Crs.CrsCod = (long) courseCode; /***** Open groups of this course that must be opened if open time is in the past *****/ Grp_OpenGroupsAutomatically (); /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /***** Check if course code is correct *****/ if (Gbl.Hierarchy.Crs.CrsCod <= 0) return soap_sender_fault (Gbl.soap, "Bad course code", "Course code must be a integer greater than 0"); /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.Hierarchy.Crs.CrsCod)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Check if I am a student, non-editing teacher or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_STD && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_NET && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_TCH) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to course"); /***** Query group types in a course from database *****/ NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get group types", "SELECT GrpTypCod," "GrpTypName," "Mandatory," "Multiple," "UNIX_TIMESTAMP(OpenTime)" " FROM crs_grp_types" " WHERE CrsCod=%d" " ORDER BY GrpTypName", courseCode); getGroupTypesOut->numGroupTypes = (int) NumRows; getGroupTypesOut->groupTypesArray.__size = (int) NumRows; if (NumRows == 0) getGroupTypesOut->groupTypesArray.__ptr = NULL; else // Groups found { getGroupTypesOut->groupTypesArray.__ptr = soap_malloc (Gbl.soap,(getGroupTypesOut->groupTypesArray.__size) * sizeof (*(getGroupTypesOut->groupTypesArray.__ptr))); for (NumRow = 0; NumRow < NumRows; NumRow++) { /* Get next group */ row = mysql_fetch_row (mysql_res); /* Get group type code (row[0]) */ getGroupTypesOut->groupTypesArray.__ptr[NumRow].groupTypeCode = (int) Str_ConvertStrCodToLongCod (row[0]); /* Get group type name (row[1]) */ getGroupTypesOut->groupTypesArray.__ptr[NumRow].groupTypeName = (char *) soap_malloc (Gbl.soap,Grp_MAX_BYTES_GROUP_TYPE_NAME + 1); Str_Copy (getGroupTypesOut->groupTypesArray.__ptr[NumRow].groupTypeName,row[1], Grp_MAX_BYTES_GROUP_TYPE_NAME); /* Get whether enrolment is mandatory ('Y') or voluntary ('N') (row[2]) */ getGroupTypesOut->groupTypesArray.__ptr[NumRow].mandatory = (row[2][0] == 'Y') ? 1 : 0; /* Get whether user can enrol in multiple groups ('Y') or only in one group ('N') (row[3]) */ getGroupTypesOut->groupTypesArray.__ptr[NumRow].multiple = (row[3][0] == 'Y') ? 1 : 0; /* Get time of opening (row[4]) */ OpenTime = 0L; if (row[4]) sscanf (row[4],"%ld",&OpenTime); getGroupTypesOut->groupTypesArray.__ptr[NumRow].openTime = OpenTime; } } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); return SOAP_OK; } /*****************************************************************************/ /************************** Return groups in a course ************************/ /*****************************************************************************/ int swad__getGroups (struct soap *soap, char *wsKey,int courseCode, // input struct swad__getGroupsOutput *getGroupsOut) // output { int ReturnCode; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow,NumRows; long GrpCod; unsigned MaxStudents; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_getGroups; Gbl.Hierarchy.Crs.CrsCod = (long) courseCode; /***** Open groups of this course that must be opened if open time is in the past *****/ Grp_OpenGroupsAutomatically (); /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /***** Check if course code is correct *****/ if (Gbl.Hierarchy.Crs.CrsCod <= 0) return soap_sender_fault (Gbl.soap, "Bad course code", "Course code must be a integer greater than 0"); /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.Hierarchy.Crs.CrsCod)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Check if I am a student, non-editing teacher or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_STD && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_NET && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_TCH) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to course"); /***** Query groups in a course from database *****/ NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get user's groups", "SELECT crs_grp_types.GrpTypCod," "crs_grp_types.GrpTypName," "crs_grp.GrpCod," "crs_grp.GrpName," "crs_grp.MaxStudents," "crs_grp.Open," "crs_grp.FileZones" " FROM crs_grp_types,crs_grp" " WHERE crs_grp_types.CrsCod=%d" " AND crs_grp_types.GrpTypCod=crs_grp.GrpTypCod" " ORDER BY crs_grp_types.GrpTypName," "crs_grp.GrpName", courseCode); getGroupsOut->numGroups = (int) NumRows; getGroupsOut->groupsArray.__size = (int) NumRows; if (NumRows == 0) getGroupsOut->groupsArray.__ptr = NULL; else // Groups found { getGroupsOut->groupsArray.__ptr = soap_malloc (Gbl.soap,(getGroupsOut->groupsArray.__size) * sizeof (*(getGroupsOut->groupsArray.__ptr))); for (NumRow = 0; NumRow < NumRows; NumRow++) { /* Get next group */ row = mysql_fetch_row (mysql_res); /* Get group type code (row[0]) */ getGroupsOut->groupsArray.__ptr[NumRow].groupTypeCode = (int) Str_ConvertStrCodToLongCod (row[0]); /* Get group type name (row[1]) */ getGroupsOut->groupsArray.__ptr[NumRow].groupTypeName = (char *) soap_malloc (Gbl.soap,Grp_MAX_BYTES_GROUP_TYPE_NAME + 1); Str_Copy (getGroupsOut->groupsArray.__ptr[NumRow].groupTypeName,row[1], Grp_MAX_BYTES_GROUP_TYPE_NAME); /* Get group code (row[2]) */ GrpCod = Str_ConvertStrCodToLongCod (row[2]); getGroupsOut->groupsArray.__ptr[NumRow].groupCode = (int) GrpCod; /* Get group name (row[3]) */ getGroupsOut->groupsArray.__ptr[NumRow].groupName = (char *) soap_malloc (Gbl.soap,Grp_MAX_BYTES_GROUP_NAME + 1); Str_Copy (getGroupsOut->groupsArray.__ptr[NumRow].groupName,row[3], Grp_MAX_BYTES_GROUP_NAME); /* Get max number of students of group (row[4]) and number of current students */ MaxStudents = Grp_ConvertToNumMaxStdsGrp (row[4]); getGroupsOut->groupsArray.__ptr[NumRow].maxStudents = (MaxStudents > Grp_MAX_STUDENTS_IN_A_GROUP) ? -1 : (int) MaxStudents; /* Get number of current students */ getGroupsOut->groupsArray.__ptr[NumRow].numStudents = (int) Grp_CountNumUsrsInGrp (Rol_STD,GrpCod); /* Get whether group is open ('Y') or closed ('N') (row[5]) */ getGroupsOut->groupsArray.__ptr[NumRow].open = (row[5][0] == 'Y') ? 1 : 0; /* Get whether group have file zones ('Y') or not ('N') (row[6]) */ getGroupsOut->groupsArray.__ptr[NumRow].fileZones = (row[6][0] == 'Y') ? 1 : 0; /* Get whether I belong to this group or not */ getGroupsOut->groupsArray.__ptr[NumRow].member = Grp_GetIfIBelongToGrp (GrpCod) ? 1 : 0; } } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); return SOAP_OK; } /*****************************************************************************/ /************************* Send my groups in a course ************************/ /*****************************************************************************/ int swad__sendMyGroups (struct soap *soap, char *wsKey,int courseCode,char *myGroups, // input struct swad__sendMyGroupsOutput *SendMyGroupsOut) // output { int ReturnCode; struct ListCodGrps LstGrpsIWant; const char *Ptr; char LongStr[1 + 10 + 1]; unsigned NumGrp; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow; unsigned NumRows; long GrpCod; unsigned MaxStudents; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_sendMyGroups; Gbl.Hierarchy.Crs.CrsCod = (long) courseCode; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /***** Check if course code is correct *****/ if (Gbl.Hierarchy.Crs.CrsCod <= 0) return soap_sender_fault (Gbl.soap, "Bad course code", "Course code must be a integer greater than 0"); /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.Hierarchy.Crs.CrsCod)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Check if I am a student, non-editing teacher or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_STD && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_NET && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_TCH) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to course"); /***** Get the group codes which I want to join to *****/ LstGrpsIWant.NumGrps = 0; LstGrpsIWant.GrpCods = NULL; if (myGroups[0]) { /***** Count number of desired groups *****/ for (NumGrp = 0, Ptr = myGroups; *Ptr; NumGrp++) Str_GetNextStringUntilComma (&Ptr,LongStr,1 + 10); LstGrpsIWant.NumGrps = NumGrp; if (LstGrpsIWant.NumGrps) // If I have selected groups... { /***** Create a list of groups selected from myGroups *****/ if ((LstGrpsIWant.GrpCods = (long *) calloc (LstGrpsIWant.NumGrps,sizeof (long))) == NULL) Lay_NotEnoughMemoryExit (); for (NumGrp = 0, Ptr = myGroups; *Ptr; NumGrp++) { /* Find next string in text until comma (leading and trailing spaces are removed) */ Str_GetNextStringUntilComma (&Ptr,LongStr,1 + 10); LstGrpsIWant.GrpCods[NumGrp] = Str_ConvertStrCodToLongCod (LongStr); } } } /***** Change my groups *****/ SendMyGroupsOut->success = Grp_ChangeMyGrpsAtomically (&LstGrpsIWant); /***** Free memory with the list of groups which I want to belong to *****/ Grp_FreeListCodGrp (&LstGrpsIWant); /***** Query groups in a course from database *****/ NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get user's groups", "SELECT crs_grp_types.GrpTypCod," "crs_grp_types.GrpTypName," "crs_grp.GrpCod," "crs_grp.GrpName," "crs_grp.MaxStudents," "crs_grp.Open," "crs_grp.FileZones" " FROM crs_grp_types,crs_grp" " WHERE crs_grp_types.CrsCod=%d" " AND crs_grp_types.GrpTypCod=crs_grp.GrpTypCod" " ORDER BY crs_grp_types.GrpTypName,crs_grp.GrpName", courseCode); SendMyGroupsOut->numGroups = (int) NumRows; SendMyGroupsOut->groupsArray.__size = (int) NumRows; if (NumRows == 0) SendMyGroupsOut->groupsArray.__ptr = NULL; else // Groups found { SendMyGroupsOut->groupsArray.__ptr = soap_malloc (Gbl.soap,(SendMyGroupsOut->groupsArray.__size) * sizeof (*(SendMyGroupsOut->groupsArray.__ptr))); for (NumRow = 0; NumRow < NumRows; NumRow++) { /* Get next group */ row = mysql_fetch_row (mysql_res); /* Get group type code (row[0]) */ SendMyGroupsOut->groupsArray.__ptr[NumRow].groupTypeCode = (int) Str_ConvertStrCodToLongCod (row[0]); /* Get group type name (row[1]) */ SendMyGroupsOut->groupsArray.__ptr[NumRow].groupTypeName = (char *) soap_malloc (Gbl.soap,Grp_MAX_BYTES_GROUP_TYPE_NAME + 1); Str_Copy (SendMyGroupsOut->groupsArray.__ptr[NumRow].groupTypeName,row[1], Grp_MAX_BYTES_GROUP_TYPE_NAME); /* Get group code (row[2]) */ GrpCod = Str_ConvertStrCodToLongCod (row[2]); SendMyGroupsOut->groupsArray.__ptr[NumRow].groupCode = (int) GrpCod; /* Get group name (row[3]) */ SendMyGroupsOut->groupsArray.__ptr[NumRow].groupName = (char *) soap_malloc (Gbl.soap,Grp_MAX_BYTES_GROUP_NAME + 1); Str_Copy (SendMyGroupsOut->groupsArray.__ptr[NumRow].groupName,row[3], Grp_MAX_BYTES_GROUP_NAME); /* Get max number of students of group (row[4]) and number of current students */ MaxStudents = Grp_ConvertToNumMaxStdsGrp (row[4]); SendMyGroupsOut->groupsArray.__ptr[NumRow].maxStudents = (MaxStudents > Grp_MAX_STUDENTS_IN_A_GROUP) ? -1 : (int) MaxStudents; /* Get number of current students */ SendMyGroupsOut->groupsArray.__ptr[NumRow].numStudents = (int) Grp_CountNumUsrsInGrp (Rol_STD,GrpCod); /* Get whether group is open ('Y') or closed ('N') (row[5]) */ SendMyGroupsOut->groupsArray.__ptr[NumRow].open = (row[5][0] == 'Y') ? 1 : 0; /* Get whether group have file zones ('Y') or not ('N') (row[6]) */ SendMyGroupsOut->groupsArray.__ptr[NumRow].fileZones = (row[6][0] == 'Y') ? 1 : 0; /* Get whether I belong to this group or not */ SendMyGroupsOut->groupsArray.__ptr[NumRow].member = Grp_GetIfIBelongToGrp (GrpCod) ? 1 : 0; } } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); return SOAP_OK; } /*****************************************************************************/ /*********** Copy data of a user from database to soap structure *************/ /*****************************************************************************/ static void Svc_CopyUsrData (struct swad__user *Usr,struct UsrData *UsrDat,bool UsrIDIsVisible) { char PhotoURL[Cns_MAX_BYTES_WWW + 1]; const char *FirstID; size_t Length; /* Copy user's code */ Usr->userCode = UsrDat->UsrCod; /* Copy user's nickname */ Length = strlen (UsrDat->Nickname); Usr->userNickname = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (Usr->userNickname,UsrDat->Nickname, Length); /* Copy user's first ID */ if (UsrIDIsVisible && UsrDat->IDs.List) FirstID = UsrDat->IDs.List[0].ID; else // Hide user's ID FirstID = "********"; Length = strlen (FirstID); Usr->userID = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (Usr->userID,FirstID, Length); /* Copy user's surname1 */ Length = strlen (UsrDat->Surname1); Usr->userSurname1 = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (Usr->userSurname1,UsrDat->Surname1, Length); /* Copy user's surname2 */ Length = strlen (UsrDat->Surname2); Usr->userSurname2 = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (Usr->userSurname2,UsrDat->Surname2, Length); /* Copy user's first name */ Length = strlen (UsrDat->FirstName); Usr->userFirstname = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (Usr->userFirstname,UsrDat->FirstName, Length); /* User's photo URL */ Pho_BuildLinkToPhoto (UsrDat,PhotoURL); Length = strlen (PhotoURL); Usr->userPhoto = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (Usr->userPhoto,PhotoURL, Length); } /*****************************************************************************/ /***************** Return list of attendance events in a course **************/ /*****************************************************************************/ int swad__getAttendanceEvents (struct soap *soap, char *wsKey,int courseCode, // input struct swad__getAttendanceEventsOutput *getAttendanceEventsOut) // output { int ReturnCode; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRows; int NumAttEvent; long AttCod; char PhotoURL[Cns_MAX_BYTES_WWW + 1]; long StartTime; long EndTime; size_t Length; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_getAttendanceEvents; Gbl.Hierarchy.Crs.CrsCod = (long) courseCode; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /***** Check if course code is correct *****/ if (Gbl.Hierarchy.Crs.CrsCod <= 0) return soap_sender_fault (Gbl.soap, "Bad course code", "Course code must be a integer greater than 0"); /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.Hierarchy.Crs.CrsCod)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Check if I am a teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_TCH) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must be a teacher"); /***** Query list of attendance events *****/ NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get attendance events", "SELECT AttCod,Hidden,UsrCod," "UNIX_TIMESTAMP(StartTime) AS ST," "UNIX_TIMESTAMP(EndTime) AS ET," "CommentTchVisible,Title,Txt" " FROM att_events" " WHERE CrsCod=%d" " ORDER BY ST DESC,ET DESC,Title DESC", courseCode); getAttendanceEventsOut->eventsArray.__size = getAttendanceEventsOut->numEvents = (int) NumRows; if (getAttendanceEventsOut->numEvents == 0) getAttendanceEventsOut->eventsArray.__ptr = NULL; else // Events found { getAttendanceEventsOut->eventsArray.__ptr = soap_malloc (Gbl.soap,(getAttendanceEventsOut->eventsArray.__size) * sizeof (*(getAttendanceEventsOut->eventsArray.__ptr))); for (NumAttEvent = 0; NumAttEvent < getAttendanceEventsOut->numEvents; NumAttEvent++) { /* Get next group */ row = mysql_fetch_row (mysql_res); /* Get attendance event code (row[0]) */ AttCod = Str_ConvertStrCodToLongCod (row[0]); getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].attendanceEventCode = (int) AttCod; /* Get whether the attendance event is hidden or not (row[1]) */ getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].hidden = (row[1][0] == 'Y') ? 1 : 0; /* Get user's code of the user who created the event (row[2]) */ Gbl.Usrs.Other.UsrDat.UsrCod = Str_ConvertStrCodToLongCod (row[2]); if (Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,Gbl.Hierarchy.Crs.CrsCod)) // Get some user's data from database { Length = strlen (Gbl.Usrs.Other.UsrDat.Surname1); getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].userSurname1 = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].userSurname1, Gbl.Usrs.Other.UsrDat.Surname1, Length); Length = strlen (Gbl.Usrs.Other.UsrDat.Surname2); getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].userSurname2 = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].userSurname2, Gbl.Usrs.Other.UsrDat.Surname2, Length); Length = strlen (Gbl.Usrs.Other.UsrDat.FirstName); getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].userFirstname = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].userFirstname, Gbl.Usrs.Other.UsrDat.FirstName, Length); Pho_BuildLinkToPhoto (&Gbl.Usrs.Other.UsrDat,PhotoURL); Length = strlen (PhotoURL); getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].userPhoto = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].userPhoto, PhotoURL, Length); } else { getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].userSurname1 = NULL; getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].userSurname2 = NULL; getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].userFirstname = NULL; getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].userPhoto = NULL; } /* Get event start time (row[3]) */ StartTime = 0L; if (row[3]) sscanf (row[3],"%ld",&StartTime); getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].startTime = StartTime; /* Get event end time (row[4]) */ EndTime = 0L; if (row[4]) sscanf (row[4],"%ld",&EndTime); getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].endTime = EndTime; /* Get whether teachers comments are visible ('Y') or hidden ('N') (row[5]) */ getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].commentsTeachersVisible = (row[5][0] == 'Y') ? 1 : 0; /* Get title of the event (row[6]) */ Length = strlen (row[6]); getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].title = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].title,row[6], Length); /* Get Txt (row[7]) */ Length = strlen (row[7]); getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].text = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].text,row[7], Length); /* Get list of groups for this attendance event */ Svc_GetListGrpsInAttendanceEventFromDB (AttCod,&(getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].groups)); } } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); return SOAP_OK; } /*****************************************************************************/ /**************** Get lists of groups of an attendance event *****************/ /*****************************************************************************/ static void Svc_GetListGrpsInAttendanceEventFromDB (long AttCod,char **ListGroups) { MYSQL_RES *mysql_res; MYSQL_ROW row; long NumGrps; long NumGrp; long GrpCod; char GrpCodStr[10 + 1]; size_t Length; /***** Get list of groups *****/ NumGrps = (unsigned) DB_QuerySELECT (&mysql_res,"can not get groups" " of an attendance event", "SELECT GrpCod FROM att_grp WHERE AttCod=%ld", AttCod); if (NumGrps == 0) *ListGroups = NULL; else // Events found { Length = NumGrps * (10 + 1) - 1; *ListGroups = soap_malloc (Gbl.soap,Length + 1); (*ListGroups)[0] = '\0'; for (NumGrp = 0; NumGrp < NumGrps; NumGrp++) { /* Get next group */ row = mysql_fetch_row (mysql_res); /* Get group code (row[0]) */ GrpCod = Str_ConvertStrCodToLongCod (row[0]); snprintf (GrpCodStr,sizeof (GrpCodStr), NumGrp ? ",%ld" : "%ld", GrpCod); Str_Concat (*ListGroups,GrpCodStr, Length); } } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); } /*****************************************************************************/ /****************** Send the data of an attendance event *********************/ /*****************************************************************************/ int swad__sendAttendanceEvent (struct soap *soap, char *wsKey,int attendanceEventCode,int courseCode,int hidden, int startTime,int endTime,int commentsTeachersVisible, char *title,char *text,char *groups, // input struct swad__sendAttendanceEventOutput *sendAttendanceEventOut) // output { int ReturnCode; struct AttendanceEvent Att; bool ItsANewAttEvent; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_sendAttendanceEvent; Gbl.Hierarchy.Crs.CrsCod = (long) courseCode; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /***** Check if course code is correct *****/ if (Gbl.Hierarchy.Crs.CrsCod <= 0) return soap_sender_fault (Gbl.soap, "Bad course code", "Course code must be a integer greater than 0"); /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.Hierarchy.Crs.CrsCod)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Check if I am a teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_TCH) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must be a teacher"); /**** Get data of attendance event *****/ /* Event code */ Att.AttCod = (long) attendanceEventCode; /* Course code */ if (Att.AttCod > 0) // The event already exists { Att_GetDataOfAttEventByCod (&Att); if (Att.CrsCod != (long) courseCode) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Attendance event does not belong to course"); ItsANewAttEvent = false; } else { ItsANewAttEvent = true; Att.CrsCod = (long) courseCode; } /* Is event hidden? */ Att.Hidden = (hidden ? true : false); /* User's code (really not used) */ Att.UsrCod = Gbl.Usrs.Me.UsrDat.UsrCod; /* startTime */ Att.TimeUTC[Att_START_TIME] = (time_t) startTime; /* endTime */ Att.TimeUTC[Att_END_TIME ] = (time_t) endTime; /* Are teacher's comments visible? */ Att.CommentTchVisible = (commentsTeachersVisible ? true : false); /* Title */ if (!title[0]) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Title of attendance event is empty"); Str_Copy (Att.Title,title, Att_MAX_BYTES_ATTENDANCE_EVENT_TITLE); /* Create a list of groups selected */ Svc_GetLstGrpsSel (groups); /***** Create or update attendance event *****/ if (ItsANewAttEvent) Att_CreateAttEvent (&Att,text); // Add new attendance event to database else Att_UpdateAttEvent (&Att,text); // Modify existing attendance event /***** Free memory for list of selected groups *****/ Grp_FreeListCodSelectedGrps (); sendAttendanceEventOut->attendanceEventCode = Att.AttCod; return SOAP_OK; } /*****************************************************************************/ /************************ Remove an attendance event *************************/ /*****************************************************************************/ int swad__removeAttendanceEvent (struct soap *soap, char *wsKey,int attendanceEventCode, // input struct swad__removeAttendanceEventOutput *removeAttendanceEventOut) // output { int ReturnCode; struct AttendanceEvent Att; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_removeAttendanceEvent; removeAttendanceEventOut->attendanceEventCode = 0; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /**** Get data of attendance event *****/ /* Event code */ Att.AttCod = (long) attendanceEventCode; /* Course code */ if (Att.AttCod > 0) // The event already exists { Att_GetDataOfAttEventByCod (&Att); Gbl.Hierarchy.Crs.CrsCod = Att.CrsCod; } else return soap_receiver_fault (Gbl.soap, "Request forbidden", "Attendance event does not exist"); /***** Check if course code is correct *****/ if (Gbl.Hierarchy.Crs.CrsCod <= 0) return soap_sender_fault (Gbl.soap, "Bad course code", "Course code must be a integer greater than 0"); /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.Hierarchy.Crs.CrsCod)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Check if I am a teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_TCH) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must be a teacher"); /***** Remove the attendance event from database *****/ Att_RemoveAttEventFromDB (Att.AttCod); removeAttendanceEventOut->attendanceEventCode = Att.AttCod; return SOAP_OK; } /*****************************************************************************/ /********************** Create a list of groups selected *********************/ /*****************************************************************************/ static void Svc_GetLstGrpsSel (const char *Groups) { const char *Ptr; char LongStr[1 + 10 + 1]; unsigned NumGrp; /***** Count number of groups *****/ for (Ptr = Groups, NumGrp = 0; *Ptr; NumGrp++) Str_GetNextStringUntilComma (&Ptr,LongStr,1 + 10); Gbl.Crs.Grps.LstGrpsSel.NumGrps = NumGrp; /***** Create a list of groups selected *****/ if (Gbl.Crs.Grps.LstGrpsSel.NumGrps) { // Here NestedCalls is always 0 Gbl.Crs.Grps.LstGrpsSel.NestedCalls++; // Here NestedCalls is always 1 if ((Gbl.Crs.Grps.LstGrpsSel.GrpCods = (long *) calloc (Gbl.Crs.Grps.LstGrpsSel.NumGrps,sizeof (long))) == NULL) Lay_NotEnoughMemoryExit (); for (Ptr = Groups, NumGrp = 0; *Ptr; ) { Str_GetNextStringUntilComma (&Ptr,LongStr,1 + 10); Gbl.Crs.Grps.LstGrpsSel.GrpCods[NumGrp] = Str_ConvertStrCodToLongCod (LongStr); if (Grp_CheckIfGroupBelongsToCourse (Gbl.Crs.Grps.LstGrpsSel.GrpCods[NumGrp],Gbl.Hierarchy.Crs.CrsCod)) NumGrp++; } Gbl.Crs.Grps.LstGrpsSel.NumGrps = NumGrp; // Update number of groups } } /*****************************************************************************/ /*********** Return a list with the users in an attendance event *************/ /*****************************************************************************/ int swad__getAttendanceUsers (struct soap *soap, char *wsKey,int attendanceEventCode, // input struct swad__getAttendanceUsersOutput *getAttendanceUsersOut) // output { int ReturnCode; struct AttendanceEvent Att; char SubQuery[512]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow,NumRows; char PhotoURL[Cns_MAX_BYTES_WWW + 1]; size_t Length; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_getAttendanceUsers; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /***** Get course of this attendance event *****/ Att.AttCod = (long) attendanceEventCode; Att_GetDataOfAttEventByCod (&Att); Gbl.Hierarchy.Crs.CrsCod = Att.CrsCod; /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.Hierarchy.Crs.CrsCod)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Check if I am a teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_TCH) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must be a teacher"); /***** Query list of attendance users *****/ if (Att_CheckIfAttEventIsAssociatedToGrps (Att.AttCod)) // Event for one or more groups // Subquery: list of users in groups of this attendance event... // ...who have no entry in attendance list of users sprintf (SubQuery,"SELECT DISTINCT crs_grp_usr.UsrCod AS UsrCod,'N' AS Present" " FROM att_grp,crs_grp,crs_grp_types,crs_usr,crs_grp_usr" " WHERE att_grp.AttCod=%ld" " AND att_grp.GrpCod=crs_grp.GrpCod" " AND crs_grp.GrpTypCod=crs_grp_types.GrpTypCod" " AND crs_grp_types.CrsCod=crs_usr.CrsCod" " AND crs_usr.Role=%u" " AND crs_usr.UsrCod=crs_grp_usr.UsrCod" " AND crs_grp_usr.GrpCod=att_grp.GrpCod" " AND crs_grp_usr.UsrCod NOT IN" " (SELECT UsrCod FROM att_usr WHERE AttCod=%ld)", Att.AttCod, (unsigned) Rol_STD, Att.AttCod); else // Event for the whole course // Subquery: list of users in the course of this attendance event... // ...who have no entry in attendance list of users sprintf (SubQuery,"SELECT crs_usr.UsrCod AS UsrCod,'N' AS Present" " FROM att_events,crs_usr" " WHERE att_events.AttCod=%ld" " AND att_events.CrsCod=crs_usr.CrsCod" " AND crs_usr.Role=%u" " AND crs_usr.UsrCod NOT IN" " (SELECT UsrCod FROM att_usr WHERE AttCod=%ld)", Att.AttCod, (unsigned) Rol_STD, Att.AttCod); // Query: list of users in attendance list + rest of users (subquery) NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get users" " in an attendance event", "SELECT u.UsrCod,u.Present FROM " "(SELECT UsrCod,Present" " FROM att_usr WHERE AttCod=%ld" " UNION %s) AS u,usr_data" " WHERE u.UsrCod=usr_data.UsrCod" " ORDER BY usr_data.Surname1," "usr_data.Surname2," "usr_data.FirstName", (long) attendanceEventCode,SubQuery); getAttendanceUsersOut->numUsers = (int) NumRows; getAttendanceUsersOut->usersArray.__size = (int) NumRows; if (NumRows == 0) getAttendanceUsersOut->usersArray.__ptr = NULL; else // Events found { getAttendanceUsersOut->usersArray.__ptr = soap_malloc (Gbl.soap,(getAttendanceUsersOut->usersArray.__size) * sizeof (*(getAttendanceUsersOut->usersArray.__ptr))); for (NumRow = 0; NumRow < NumRows; NumRow++) { /* Get next user */ row = mysql_fetch_row (mysql_res); /* Get user's code (row[0]) */ Gbl.Usrs.Other.UsrDat.UsrCod = Str_ConvertStrCodToLongCod (row[0]); getAttendanceUsersOut->usersArray.__ptr[NumRow].userCode = (int) Gbl.Usrs.Other.UsrDat.UsrCod; /* Get user's data */ if (Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,-1L)) // Get some user's data from database { Length = strlen (Gbl.Usrs.Other.UsrDat.Nickname); getAttendanceUsersOut->usersArray.__ptr[NumRow].userNickname = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (getAttendanceUsersOut->usersArray.__ptr[NumRow].userNickname, Gbl.Usrs.Other.UsrDat.Nickname, Length); if (Gbl.Usrs.Other.UsrDat.IDs.Num) { Length = strlen (Gbl.Usrs.Other.UsrDat.IDs.List[0].ID); // TODO: What user's ID? getAttendanceUsersOut->usersArray.__ptr[NumRow].userID = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (getAttendanceUsersOut->usersArray.__ptr[NumRow].userID, Gbl.Usrs.Other.UsrDat.IDs.List[0].ID, Length); } else { getAttendanceUsersOut->usersArray.__ptr[NumRow].userID = (char *) soap_malloc (Gbl.soap,1); getAttendanceUsersOut->usersArray.__ptr[NumRow].userID[0] = '\0'; } Length = strlen (Gbl.Usrs.Other.UsrDat.Surname1); getAttendanceUsersOut->usersArray.__ptr[NumRow].userSurname1 = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (getAttendanceUsersOut->usersArray.__ptr[NumRow].userSurname1, Gbl.Usrs.Other.UsrDat.Surname1, Length); Length = strlen (Gbl.Usrs.Other.UsrDat.Surname2); getAttendanceUsersOut->usersArray.__ptr[NumRow].userSurname2 = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (getAttendanceUsersOut->usersArray.__ptr[NumRow].userSurname2, Gbl.Usrs.Other.UsrDat.Surname2, Length); Length = strlen (Gbl.Usrs.Other.UsrDat.FirstName); getAttendanceUsersOut->usersArray.__ptr[NumRow].userFirstname = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (getAttendanceUsersOut->usersArray.__ptr[NumRow].userFirstname, Gbl.Usrs.Other.UsrDat.FirstName, Length); Pho_BuildLinkToPhoto (&Gbl.Usrs.Other.UsrDat,PhotoURL); Length = strlen (PhotoURL); getAttendanceUsersOut->usersArray.__ptr[NumRow].userPhoto = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (getAttendanceUsersOut->usersArray.__ptr[NumRow].userPhoto, PhotoURL, Length); } else { getAttendanceUsersOut->usersArray.__ptr[NumRow].userNickname = NULL; getAttendanceUsersOut->usersArray.__ptr[NumRow].userID = NULL; getAttendanceUsersOut->usersArray.__ptr[NumRow].userSurname1 = NULL; getAttendanceUsersOut->usersArray.__ptr[NumRow].userSurname2 = NULL; getAttendanceUsersOut->usersArray.__ptr[NumRow].userFirstname = NULL; getAttendanceUsersOut->usersArray.__ptr[NumRow].userPhoto = NULL; } /* Get if user is present or not (row[1]) */ getAttendanceUsersOut->usersArray.__ptr[NumRow].present = (row[1][0] == 'Y') ? 1 : 0; } } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); return SOAP_OK; } /*****************************************************************************/ /************ Send a list with the users in an attendance event **************/ /*****************************************************************************/ int swad__sendAttendanceUsers (struct soap *soap, char *wsKey,int attendanceEventCode, char *users,int setOthersAsAbsent, // input struct swad__sendAttendanceUsersOutput *sendAttendanceUsersOut) // output { int ReturnCode; struct AttendanceEvent Att; const char *Ptr; char LongStr[1 + 10 + 1]; struct UsrData UsrDat; unsigned NumCodsInList; char *SubQueryAllUsrs = NULL; char SubQueryOneUsr[1 + 1 + 10 + 1]; size_t Length = 0; // Initialized to avoid warning /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_sendAttendanceUsers; /***** Initialize output *****/ sendAttendanceUsersOut->success = 0; sendAttendanceUsersOut->numUsers = 0; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /***** Get course of this attendance event *****/ Att.AttCod = (long) attendanceEventCode; if (!Att_GetDataOfAttEventByCod (&Att)) return SOAP_OK; // return with success = 0 Gbl.Hierarchy.Crs.CrsCod = Att.CrsCod; /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.Hierarchy.Crs.CrsCod)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Check if I am a teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_TCH) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must be a teacher"); /***** Initialize structure with user's data *****/ Usr_UsrDataConstructor (&UsrDat); if (setOthersAsAbsent) { /* Count number of codes in list */ for (Ptr = users, NumCodsInList = 0; *Ptr; NumCodsInList++) /* Find next string in text until comma (leading and trailing spaces are removed) */ Str_GetNextStringUntilComma (&Ptr,LongStr,1 + 10); /* Allocate subquery used to mark not present users as absent */ Length = 256 + NumCodsInList * (1 + 1 + 10 + 1) - 1; if ((SubQueryAllUsrs = (char *) malloc (Length + 1)) == NULL) return soap_receiver_fault (Gbl.soap, "Not enough memory", "Not enough memory to store list of users"); SubQueryAllUsrs[0] = '\0'; } for (Ptr = users; *Ptr; ) { /* Find next string in text until comma (leading and trailing spaces are removed) */ Str_GetNextStringUntilComma (&Ptr,LongStr,1 + 10); if ((UsrDat.UsrCod = Str_ConvertStrCodToLongCod (LongStr)) > 0) if (Usr_ChkIfUsrCodExists (UsrDat.UsrCod)) // The user must belong to course, // but it's not necessary he/she belongs to groups associated to the event if (Usr_CheckIfUsrBelongsToCurrentCrs (&UsrDat)) { /* Mark user as present */ Att_RegUsrInAttEventNotChangingComments (Att.AttCod,UsrDat.UsrCod); /* Add this user to query used to mark not present users as absent */ if (setOthersAsAbsent) { if (sendAttendanceUsersOut->numUsers) { snprintf (SubQueryOneUsr,sizeof (SubQueryOneUsr), ",%ld",UsrDat.UsrCod); Str_Concat (SubQueryAllUsrs,SubQueryOneUsr, Length); } else snprintf (SubQueryAllUsrs,Length, " AND UsrCod NOT IN (%ld", UsrDat.UsrCod); } sendAttendanceUsersOut->numUsers++; } } if (setOthersAsAbsent) { /* Mark not present users as absent in table of users */ if (sendAttendanceUsersOut->numUsers) Str_Concat (SubQueryAllUsrs,")", Length); DB_QueryUPDATE ("can not set other users as absent", "UPDATE att_usr SET Present='N'" " WHERE AttCod=%ld%s", Att.AttCod,SubQueryAllUsrs); /* Free memory for subquery string */ free ((void *) SubQueryAllUsrs); /* Clean table att_usr */ Att_RemoveUsrsAbsentWithoutCommentsFromAttEvent (Att.AttCod); } /***** Free memory used for user's data *****/ Usr_UsrDataDestructor (&UsrDat); sendAttendanceUsersOut->success = 1; return SOAP_OK; } /*****************************************************************************/ /********************* Return notifications of a user ************************/ /*****************************************************************************/ int swad__getNotifications (struct soap *soap, char *wsKey,long beginTime, // input struct swad__getNotificationsOutput *getNotificationsOut) // output { extern const char *Ntf_WSNotifyEvents[Ntf_NUM_NOTIFY_EVENTS]; extern const char *Txt_Forum; extern const char *Txt_Course; extern const char *Txt_Degree; extern const char *Txt_Centre; extern const char *Txt_Institution; int ReturnCode; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumNotifications; unsigned NumNotif; long NtfCod; Ntf_NotifyEvent_t NotifyEvent; long EventTime; char PhotoURL[Cns_MAX_BYTES_WWW + 1]; struct Instit Ins; struct Centre Ctr; struct Degree Deg; struct Course Crs; long Cod; struct Forum ForumSelected; char ForumName[For_MAX_BYTES_FORUM_NAME + 1]; char SummaryStr[Ntf_MAX_BYTES_SUMMARY + 1]; char *ContentStr; Ntf_Status_t Status; size_t Length; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_getNotifications; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,-1L)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Get my language from database *****/ if ((ReturnCode = Svc_GetMyLanguage ()) != SOAP_OK) return ReturnCode; /***** Get my notifications from database *****/ NumNotifications = (unsigned) DB_QuerySELECT (&mysql_res,"can not get user's notifications", "SELECT NtfCod," // row[0] "NotifyEvent," // row[1] "UNIX_TIMESTAMP(TimeNotif)," // row[2] "FromUsrCod," // row[3] "InsCod," // row[4] "CtrCod," // row[5] "DegCod," // row[6] "CrsCod," // row[7] "Cod," // row[8] "Status" // row[9] " FROM notif" " WHERE ToUsrCod=%ld AND TimeNotif>=FROM_UNIXTIME(%ld)" " ORDER BY TimeNotif DESC", Gbl.Usrs.Me.UsrDat.UsrCod,beginTime); if (NumNotifications) // Notifications found { getNotificationsOut->numNotifications = (int) NumNotifications; getNotificationsOut->notificationsArray.__size = (int) NumNotifications; getNotificationsOut->notificationsArray.__ptr = soap_malloc (Gbl.soap,(getNotificationsOut->notificationsArray.__size) * sizeof (*(getNotificationsOut->notificationsArray.__ptr))); for (NumNotif = 0; NumNotif < NumNotifications; NumNotif++) { /* Get next notification */ row = mysql_fetch_row (mysql_res); /* Get unique notification code (row[0]) */ NtfCod = Str_ConvertStrCodToLongCod (row[0]); getNotificationsOut->notificationsArray.__ptr[NumNotif].notifCode = (int) NtfCod; /* Get notification event type (row[1]) */ NotifyEvent = Ntf_GetNotifyEventFromDB ((const char *) row[1]); getNotificationsOut->notificationsArray.__ptr[NumNotif].eventType = (char *) soap_malloc (Gbl.soap,Ntf_MAX_BYTES_NOTIFY_EVENT + 1); Str_Copy (getNotificationsOut->notificationsArray.__ptr[NumNotif].eventType, Ntf_WSNotifyEvents[NotifyEvent], Ntf_MAX_BYTES_NOTIFY_EVENT); /* Get time of the event (row[2]) */ EventTime = 0L; if (row[2]) sscanf (row[2],"%ld",&EventTime); getNotificationsOut->notificationsArray.__ptr[NumNotif].eventTime = EventTime; /* Get course (row[7]) */ Crs.CrsCod = Str_ConvertStrCodToLongCod (row[7]); Crs_GetDataOfCourseByCod (&Crs); /* Get user's code of the user who caused the event (row[3]) */ Gbl.Usrs.Other.UsrDat.UsrCod = Str_ConvertStrCodToLongCod (row[3]); if (Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,Crs.CrsCod)) // Get some user's data from database { getNotificationsOut->notificationsArray.__ptr[NumNotif].userNickname = (char *) soap_malloc (Gbl.soap,Nck_MAX_BYTES_NICKNAME_WITHOUT_ARROBA + 1); Str_Copy (getNotificationsOut->notificationsArray.__ptr[NumNotif].userNickname, Gbl.Usrs.Other.UsrDat.Nickname, Nck_MAX_BYTES_NICKNAME_WITHOUT_ARROBA); getNotificationsOut->notificationsArray.__ptr[NumNotif].userSurname1 = (char *) soap_malloc (Gbl.soap,Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME + 1); Str_Copy (getNotificationsOut->notificationsArray.__ptr[NumNotif].userSurname1, Gbl.Usrs.Other.UsrDat.Surname1, Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME); getNotificationsOut->notificationsArray.__ptr[NumNotif].userSurname2 = (char *) soap_malloc (Gbl.soap,Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME + 1); Str_Copy (getNotificationsOut->notificationsArray.__ptr[NumNotif].userSurname2, Gbl.Usrs.Other.UsrDat.Surname2, Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME); getNotificationsOut->notificationsArray.__ptr[NumNotif].userFirstname = (char *) soap_malloc (Gbl.soap,Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME + 1); Str_Copy (getNotificationsOut->notificationsArray.__ptr[NumNotif].userFirstname, Gbl.Usrs.Other.UsrDat.FirstName, Usr_MAX_BYTES_FIRSTNAME_OR_SURNAME); Pho_BuildLinkToPhoto (&Gbl.Usrs.Other.UsrDat,PhotoURL); getNotificationsOut->notificationsArray.__ptr[NumNotif].userPhoto = (char *) soap_malloc (Gbl.soap,Cns_MAX_BYTES_WWW + 1); Str_Copy (getNotificationsOut->notificationsArray.__ptr[NumNotif].userPhoto, PhotoURL, Cns_MAX_BYTES_WWW); } else { getNotificationsOut->notificationsArray.__ptr[NumNotif].userNickname = NULL; getNotificationsOut->notificationsArray.__ptr[NumNotif].userSurname1 = NULL; getNotificationsOut->notificationsArray.__ptr[NumNotif].userSurname2 = NULL; getNotificationsOut->notificationsArray.__ptr[NumNotif].userFirstname = NULL; getNotificationsOut->notificationsArray.__ptr[NumNotif].userPhoto = NULL; } /* Get institution (row[4]) */ Ins.InsCod = Str_ConvertStrCodToLongCod (row[4]); Ins_GetDataOfInstitutionByCod (&Ins,Ins_GET_BASIC_DATA); /* Get centre (row[5]) */ Ctr.CtrCod = Str_ConvertStrCodToLongCod (row[5]); Ctr_GetDataOfCentreByCod (&Ctr); /* Get degree (row[6]) */ Deg.DegCod = Str_ConvertStrCodToLongCod (row[6]); Deg_GetDataOfDegreeByCod (&Deg); /* Get message/post/... code (row[8]) */ Cod = Str_ConvertStrCodToLongCod (row[8]); getNotificationsOut->notificationsArray.__ptr[NumNotif].eventCode = (int) Cod; /* Set location */ getNotificationsOut->notificationsArray.__ptr[NumNotif].location = (char *) soap_malloc (Gbl.soap,Ntf_MAX_BYTES_NOTIFY_LOCATION + 1); if (NotifyEvent == Ntf_EVENT_FORUM_POST_COURSE || NotifyEvent == Ntf_EVENT_FORUM_REPLY) { For_GetForumTypeAndLocationOfAPost (Cod,&ForumSelected); For_SetForumName (&ForumSelected, ForumName,Gbl.Prefs.Language,false); // Set forum name in recipient's language sprintf (getNotificationsOut->notificationsArray.__ptr[NumNotif].location,"%s: %s", Txt_Forum,ForumName); } else if (Crs.CrsCod > 0) sprintf (getNotificationsOut->notificationsArray.__ptr[NumNotif].location,"%s: %s", Txt_Course,Crs.ShrtName); else if (Deg.DegCod > 0) sprintf (getNotificationsOut->notificationsArray.__ptr[NumNotif].location,"%s: %s", Txt_Degree,Deg.ShrtName); else if (Ctr.CtrCod > 0) sprintf (getNotificationsOut->notificationsArray.__ptr[NumNotif].location,"%s: %s", Txt_Centre,Ctr.ShrtName); else if (Ins.InsCod > 0) sprintf (getNotificationsOut->notificationsArray.__ptr[NumNotif].location,"%s: %s", Txt_Institution,Ins.ShrtName); else Str_Copy (getNotificationsOut->notificationsArray.__ptr[NumNotif].location,"-", Ntf_MAX_BYTES_NOTIFY_LOCATION); /* Get status (row[9]) */ if (sscanf (row[9],"%u",&Status) != 1) Status = (Ntf_Status_t) 0; getNotificationsOut->notificationsArray.__ptr[NumNotif].status = (int) Status; /* Get summary and content */ ContentStr = NULL; Ntf_GetNotifSummaryAndContent (SummaryStr,&ContentStr,NotifyEvent, Cod,Crs.CrsCod,Gbl.Usrs.Me.UsrDat.UsrCod, true); Length = strlen (SummaryStr); getNotificationsOut->notificationsArray.__ptr[NumNotif].summary = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (getNotificationsOut->notificationsArray.__ptr[NumNotif].summary, SummaryStr, Length); if (ContentStr == NULL) { getNotificationsOut->notificationsArray.__ptr[NumNotif].content = (char *) soap_malloc (Gbl.soap,1); getNotificationsOut->notificationsArray.__ptr[NumNotif].content[0] = '\0'; } else { Length = strlen (ContentStr); getNotificationsOut->notificationsArray.__ptr[NumNotif].content = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (getNotificationsOut->notificationsArray.__ptr[NumNotif].content, ContentStr, Length); /* Free memory used by content string */ free ((void *) ContentStr); ContentStr = NULL; } } } else // No notifications found { getNotificationsOut->numNotifications = 0; getNotificationsOut->notificationsArray.__size = 0; getNotificationsOut->notificationsArray.__ptr = NULL; } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); return SOAP_OK; } /*****************************************************************************/ /********************* Get my language from database *************************/ /*****************************************************************************/ static int Svc_GetMyLanguage (void) { extern const char *Lan_STR_LANG_ID[1 + Lan_NUM_LANGUAGES]; MYSQL_RES *mysql_res; MYSQL_ROW row; Lan_Language_t Lan; /***** Get user's language *****/ if (DB_QuerySELECT (&mysql_res,"can not get user's language", "SELECT Language FROM usr_data" " WHERE UsrCod=%ld", Gbl.Usrs.Me.UsrDat.UsrCod) != 1) return soap_receiver_fault (Gbl.soap, "Can not get user's language from database", "User doen't exist in database"); /***** Get language from database *****/ row = mysql_fetch_row (mysql_res); /* Get language (row[0]) */ Gbl.Prefs.Language = Lan_LANGUAGE_UNKNOWN; // Language unknown for (Lan = (Lan_Language_t) 1; Lan <= Lan_NUM_LANGUAGES; Lan++) if (!strcasecmp (row[0],Lan_STR_LANG_ID[Lan])) { Gbl.Prefs.Language = Lan; break; } if (Gbl.Prefs.Language == Lan_LANGUAGE_UNKNOWN) // Language stored in database is unknown Gbl.Prefs.Language = Cfg_DEFAULT_LANGUAGE; /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); return SOAP_OK; } /*****************************************************************************/ /************************* Send a notice to a course *************************/ /*****************************************************************************/ int swad__markNotificationsAsRead (struct soap *soap, char *wsKey,char *notifications, // input struct swad__markNotificationsAsReadOutput *markNotificationsAsReadOut) // output { int ReturnCode; const char *Ptr; char LongStr[1 + 10 + 1]; unsigned NumNtf; unsigned NumNtfsMarkedAsRead = 0; long NtfCod; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_markNotificationsAsRead; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,-1L)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; if (notifications[0]) { /***** Mark notifications from list as read *****/ for (NumNtf = 0, Ptr = notifications; *Ptr; NumNtf++) { /* Find next string in text until comma (leading and trailing spaces are removed) */ Str_GetNextStringUntilComma (&Ptr,LongStr,1 + 10); if ((NtfCod = Str_ConvertStrCodToLongCod (LongStr)) > 0) { /***** Mark notification as read in the database *****/ DB_QueryUPDATE ("can not mark notification as read", "UPDATE notif SET Status=(Status | %u)" " WHERE NtfCod=%ld AND ToUsrCod=%ld", (unsigned) Ntf_STATUS_BIT_READ, (long) NtfCod,Gbl.Usrs.Me.UsrDat.UsrCod); NumNtfsMarkedAsRead++; } } } /***** Return notification code *****/ markNotificationsAsReadOut->numNotifications = (int) NumNtfsMarkedAsRead; return SOAP_OK; } /*****************************************************************************/ /****************** Send a message to one or more users **********************/ /*****************************************************************************/ #define Svc_MAX_BYTES_QUERY_RECIPIENTS (10 * 1024 - 1) int swad__sendMessage (struct soap *soap, char *wsKey,int messageCode,char *to,char *subject,char *body, // input struct swad__sendMessageOutput *sendMessageOut) // output { int ReturnCode; long ReplyUsrCod = -1L; char Nickname[Nck_MAX_BYTES_NICKNAME_FROM_FORM + 1]; char *Query = NULL; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow; unsigned NumRows; bool FirstNickname = true; bool ThereAreNicknames = false; const char *Ptr; bool ItsMe; bool NotifyByEmail; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_sendMessage; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,-1L)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Check if the message is a reply to a previous message *****/ if (messageCode) { /***** Check if the original message was really received by me *****/ if (!DB_QuerySELECT (&mysql_res,"can not check original message", "SELECT SUM(N) FROM" " (SELECT COUNT(*) AS N FROM msg_rcv" " WHERE UsrCod=%ld AND MsgCod=%ld" " UNION" " SELECT COUNT(*) AS N FROM msg_rcv_deleted" " WHERE UsrCod=%ld AND MsgCod=%ld) AS T", Gbl.Usrs.Me.UsrDat.UsrCod,(long) messageCode, Gbl.Usrs.Me.UsrDat.UsrCod,(long) messageCode)) return soap_sender_fault (Gbl.soap, "Can not check original message", "Error reading from database"); /***** Get number of rows *****/ row = mysql_fetch_row (mysql_res); if (sscanf (row[0],"%u",&NumRows) != 1) return soap_sender_fault (Gbl.soap, "Can not check original message", "Error reading from database"); /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); if (!NumRows) return soap_sender_fault (Gbl.soap, "Can not send reply message", "Original message does not exist"); /***** Get the recipient of the message *****/ NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not check original message", "SELECT UsrCod FROM msg_snt" " WHERE MsgCod=%ld" " UNION " "SELECT UsrCod FROM msg_snt_deleted" " WHERE MsgCod=%ld", (long) messageCode,(long) messageCode); if (NumRows) // Message found in any of the two tables of sent messages { row = mysql_fetch_row (mysql_res); ReplyUsrCod = Str_ConvertStrCodToLongCod (row[0]); } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); if (!NumRows) return soap_sender_fault (Gbl.soap, "Can not send reply message", "Original message does not exist"); } /***** Allocate space for query *****/ if ((Query = (char *) malloc (Svc_MAX_BYTES_QUERY_RECIPIENTS + 1)) == NULL) Lay_NotEnoughMemoryExit (); /***** Build query for recipients from database *****/ if (ReplyUsrCod > 0) snprintf (Query,Svc_MAX_BYTES_QUERY_RECIPIENTS + 1, "SELECT UsrCod FROM usr_data" " WHERE UsrCod=%ld", ReplyUsrCod); else Query[0] = '\0'; /***** Loop over recipients' nicknames building query *****/ Ptr = to; while (*Ptr) { /* Find next string in text until comma (leading and trailing spaces are removed) */ Str_GetNextStringUntilComma (&Ptr,Nickname,Nck_MAX_BYTES_NICKNAME_FROM_FORM); /* Check if string is a valid nickname */ if (Nck_CheckIfNickWithArrobaIsValid (Nickname)) // String is a nickname? { Str_RemoveLeadingArrobas (Nickname); /* Check for overflow in query */ if (strlen (Query) + Nck_MAX_BYTES_NICKNAME_WITHOUT_ARROBA + 32 > Svc_MAX_BYTES_QUERY_RECIPIENTS) return soap_sender_fault (Gbl.soap, "Can not send message", "Too many recipients"); /* Add this nickname to query */ if (FirstNickname) { if (ReplyUsrCod > 0) Str_Concat (Query," UNION ", Svc_MAX_BYTES_QUERY_RECIPIENTS); Str_Concat (Query,"SELECT UsrCod FROM usr_nicknames" " WHERE Nickname IN ('", Svc_MAX_BYTES_QUERY_RECIPIENTS); FirstNickname = false; ThereAreNicknames = true; } else Str_Concat (Query,",'", Svc_MAX_BYTES_QUERY_RECIPIENTS); Str_Concat (Query,Nickname, Svc_MAX_BYTES_QUERY_RECIPIENTS); Str_Concat (Query,"'", Svc_MAX_BYTES_QUERY_RECIPIENTS); } } if (ThereAreNicknames) Str_Concat (Query,")", Svc_MAX_BYTES_QUERY_RECIPIENTS); /***** Initialize output structure *****/ sendMessageOut->numUsers = 0; sendMessageOut->usersArray.__size = 0; sendMessageOut->usersArray.__ptr = NULL; if (ReplyUsrCod > 0 || ThereAreNicknames) // There are a recipient to reply or nicknames in "to" { /***** Get users *****/ NumRows = DB_QuerySELECT (&mysql_res,"can not get users", "%s", Query); sendMessageOut->numUsers = (int) NumRows; sendMessageOut->usersArray.__size = (int) NumRows; if (NumRows) // Users found { sendMessageOut->usersArray.__ptr = soap_malloc (Gbl.soap,(sendMessageOut->usersArray.__size) * sizeof (*(sendMessageOut->usersArray.__ptr))); for (NumRow = 0; NumRow < NumRows; NumRow++) { /* Get next user */ row = mysql_fetch_row (mysql_res); /* Get user's code (row[0]) */ if ((Gbl.Usrs.Other.UsrDat.UsrCod = (long) Str_ConvertStrCodToLongCod (row[0])) > 0) /* Get recipient data */ if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,Usr_DONT_GET_PREFS)) { /* This received message must be notified by email? */ ItsMe = Usr_ItsMe (Gbl.Usrs.Other.UsrDat.UsrCod); NotifyByEmail = (!ItsMe && (Gbl.Usrs.Other.UsrDat.NtfEvents.SendEmail & (1 << Ntf_EVENT_MESSAGE))); /* Send message to this user */ if ((ReturnCode = Svc_SendMessageToUsr ((long) messageCode,Gbl.Usrs.Me.UsrDat.UsrCod,ReplyUsrCod,Gbl.Usrs.Other.UsrDat.UsrCod,NotifyByEmail,subject,body)) != SOAP_OK) { DB_FreeMySQLResult (&mysql_res); return ReturnCode; } /* Copy user's data into output structure */ Svc_CopyUsrData (&(sendMessageOut->usersArray.__ptr[NumRow]),&Gbl.Usrs.Other.UsrDat,false); } } } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); } return SOAP_OK; } /*****************************************************************************/ /************************* Send a message to one user ************************/ /*****************************************************************************/ /* Svc_SendMessageToUsr ((long) messageCode, Gbl.Usrs.Me.UsrDat.UsrCod,ReplyUsrCod,Gbl.Usrs.Other.UsrDat.UsrCod, NotifyByEmail,subject,body)) != SOAP_OK) */ static int Svc_SendMessageToUsr (long OriginalMsgCod, long SenderUsrCod,long ReplyUsrCod,long RecipientUsrCod, bool NotifyByEmail, const char *Subject,const char *Content) { static bool MsgAlreadyInserted = false; static long NewMsgCod; /***** Create message *****/ if (!MsgAlreadyInserted) // The message is inserted only once in the table of messages sent { /***** Insert message subject and body in the database *****/ /* Get the code of the inserted item */ NewMsgCod = DB_QueryINSERTandReturnCode ("can not create message", "INSERT INTO msg_content" " (Subject,Content,MedCod)" " VALUES" " ('%s','%s',-1)", Subject,Content); /* Insert message in sent messages */ DB_QueryINSERT ("can not create message", "INSERT INTO msg_snt" " (MsgCod,CrsCod,UsrCod,Expanded,CreatTime)" " VALUES" " (%ld,-1,%ld,'N',NOW())", NewMsgCod,SenderUsrCod); MsgAlreadyInserted = true; } /***** Insert message received in the database *****/ DB_QueryINSERT ("can not create received message", "INSERT INTO msg_rcv" " (MsgCod,UsrCod,Notified,Open,Replied,Expanded)" " VALUES" " (%ld,%ld,'%c','N','N','N')", NewMsgCod,RecipientUsrCod, NotifyByEmail ? 'Y' : 'N'); /***** Create notification for this recipient. If this recipient wants to receive notifications by email, activate the sending of a notification *****/ DB_QueryINSERT ("can not create new notification event", "INSERT INTO notif" " (NotifyEvent,ToUsrCod,FromUsrCod,InsCod,DegCod,CrsCod,Cod,TimeNotif,Status)" " VALUES" " (%u,%ld,%ld,-1,-1,-1,%ld,NOW(),%u)", (unsigned) Ntf_EVENT_MESSAGE, RecipientUsrCod, SenderUsrCod, NewMsgCod, (unsigned) (NotifyByEmail ? Ntf_STATUS_BIT_EMAIL : 0)); /***** If this recipient is the original sender of a message been replied... *****/ if (RecipientUsrCod == ReplyUsrCod) /***** ...then update received message setting Replied field to true *****/ DB_QueryUPDATE ("can not update a received message", "UPDATE msg_rcv SET Replied='Y'" " WHERE MsgCod=%ld AND UsrCod=%ld", OriginalMsgCod,SenderUsrCod); return SOAP_OK; } /*****************************************************************************/ /************************* Send a notice to a course *************************/ /*****************************************************************************/ int swad__sendNotice (struct soap *soap, char *wsKey,int courseCode,char *body, // input struct swad__sendNoticeOutput *sendNoticeOut) // output { int ReturnCode; long NotCod; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_sendNotice; Gbl.Hierarchy.Crs.CrsCod = (long) courseCode; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.Hierarchy.Crs.CrsCod)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Check course and group codes *****/ if ((ReturnCode = Svc_CheckCourseAndGroupCodes (Gbl.Hierarchy.Crs.CrsCod,-1L)) != SOAP_OK) return ReturnCode; /***** Get degree of current course *****/ if ((ReturnCode = Svc_GetCurrentDegCodFromCurrentCrsCod ()) != SOAP_OK) return ReturnCode; /***** Check if I am a teacher *****/ if (Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_TCH) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must be a teacher"); /***** Insert notice in the database *****/ /* Get the code of the inserted item */ NotCod = DB_QueryINSERTandReturnCode ("can not create message", "INSERT INTO notices" " (CrsCod,UsrCod,CreatTime,Content,Status)" " VALUES" " (%ld,%ld,NOW(),'%s',%u)", Gbl.Hierarchy.Crs.CrsCod,Gbl.Usrs.Me.UsrDat.UsrCod, body,(unsigned) Not_ACTIVE_NOTICE); /***** Create notifications *****/ // TODO: create notifications // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! /***** Return notification code *****/ sendNoticeOut->noticeCode = (int) NotCod; return SOAP_OK; } /*****************************************************************************/ /****************** Return test configuration in a course ********************/ /*****************************************************************************/ int swad__getTestConfig (struct soap *soap, char *wsKey,int courseCode, // input struct swad__getTestConfigOutput *getTestConfigOut) // output { extern const char *Tst_FeedbackXML[Tst_NUM_TYPES_FEEDBACK]; int ReturnCode; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_getTestConfig; Gbl.Hierarchy.Crs.CrsCod = (long) courseCode; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.Hierarchy.Crs.CrsCod)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Check if course code is correct *****/ if (Gbl.Hierarchy.Crs.CrsCod <= 0) return soap_sender_fault (Gbl.soap, "Bad course code", "Course code must be a integer greater than 0"); /***** Check if I am a student, non-editing teacher or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_STD && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_NET && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_TCH) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to course"); /***** Get degree of current course *****/ if ((ReturnCode = Svc_GetCurrentDegCodFromCurrentCrsCod ()) != SOAP_OK) return ReturnCode; /***** Set default result to empty *****/ getTestConfigOut->numQuestions = getTestConfigOut->minQuestions = getTestConfigOut->defQuestions = getTestConfigOut->maxQuestions = 0; getTestConfigOut->feedback = (char *) soap_malloc (Gbl.soap,Tst_MAX_BYTES_FEEDBACK_TYPE + 1); getTestConfigOut->feedback[0] = '\0'; /***** Get test configuration *****/ if ((ReturnCode = Svc_GetTstConfig ((long) courseCode)) != SOAP_OK) return ReturnCode; getTestConfigOut->pluggable = (Gbl.Test.Config.Pluggable == Tst_PLUGGABLE_YES) ? 1 : 0; getTestConfigOut->minQuestions = (int) Gbl.Test.Config.Min; getTestConfigOut->defQuestions = (int) Gbl.Test.Config.Def; getTestConfigOut->maxQuestions = (int) Gbl.Test.Config.Max; Str_Copy (getTestConfigOut->feedback, Tst_FeedbackXML[Gbl.Test.Config.Feedback], Tst_MAX_BYTES_FEEDBACK_TYPE); /***** Get number of tests *****/ if (Gbl.Test.Config.Pluggable == Tst_PLUGGABLE_YES && Gbl.Test.Config.Max > 0) getTestConfigOut->numQuestions = Svc_GetNumTestQuestionsInCrs ((long) courseCode); return SOAP_OK; } /*****************************************************************************/ /****** Get configuration of tests from database giving a course code ********/ /*****************************************************************************/ static int Svc_GetTstConfig (long CrsCod) { MYSQL_RES *mysql_res; MYSQL_ROW row; /***** Query database *****/ if (DB_QuerySELECT (&mysql_res,"can not get test configuration", "SELECT Pluggable,Min,Def,Max,MinTimeNxtTstPerQst,Feedback" " FROM tst_config WHERE CrsCod=%ld", CrsCod)) { /***** Get minimun, default and maximum *****/ row = mysql_fetch_row (mysql_res); Tst_GetConfigFromRow (row); } else // NumRows == 0 { Gbl.Test.Config.Pluggable = Tst_PLUGGABLE_UNKNOWN; Gbl.Test.Config.Min = Gbl.Test.Config.Def = Gbl.Test.Config.Max = 0; Gbl.Test.Config.Feedback = Tst_FEEDBACK_DEFAULT; } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); return SOAP_OK; } /*****************************************************************************/ /** Get number of visible test questions from database giving a course code **/ /*****************************************************************************/ static int Svc_GetNumTestQuestionsInCrs (long CrsCod) { /***** Get number of questions *****/ // Reject questions with any tag hidden // Select only questions with tags return (int) DB_QueryCOUNT ("can not get number of test questions", "SELECT COUNT(*)" " FROM tst_questions,tst_question_tags,tst_tags" " WHERE tst_questions.CrsCod=%ld" " AND tst_questions.QstCod NOT IN" " (SELECT tst_question_tags.QstCod" " FROM tst_tags,tst_question_tags" " WHERE tst_tags.CrsCod=%ld" " AND tst_tags.TagHidden='Y'" " AND tst_tags.TagCod=tst_question_tags.TagCod)" " AND tst_questions.QstCod=tst_question_tags.QstCod" " AND tst_question_tags.TagCod=tst_tags.TagCod" " AND tst_tags.CrsCod=%ld", CrsCod,CrsCod,CrsCod); } /*****************************************************************************/ /************************* Return tests of a course **************************/ /*****************************************************************************/ int swad__getTests (struct soap *soap, char *wsKey,int courseCode,long beginTime, // input struct swad__getTestsOutput *getTestsOut) // output { int ReturnCode; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_getTests; Gbl.Hierarchy.Crs.CrsCod = (long) courseCode; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.Hierarchy.Crs.CrsCod)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Check if course code is correct *****/ if (Gbl.Hierarchy.Crs.CrsCod <= 0) return soap_sender_fault (Gbl.soap, "Bad course code", "Course code must be a integer greater than 0"); /***** Check if I am a student, non-editing teacher or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_STD && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_NET && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_TCH) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to course"); /***** Get degree of current course *****/ if ((ReturnCode = Svc_GetCurrentDegCodFromCurrentCrsCod ()) != SOAP_OK) return ReturnCode; /***** Set default result to empty *****/ getTestsOut->tagsArray.__size = 0; getTestsOut->tagsArray.__ptr = NULL; getTestsOut->questionsArray.__size = 0; getTestsOut->questionsArray.__ptr = NULL; getTestsOut->answersArray.__size = 0; getTestsOut->answersArray.__ptr = NULL; getTestsOut->questionTagsArray.__size = 0; getTestsOut->questionTagsArray.__ptr = NULL; /***** Get test configuration *****/ if ((ReturnCode = Svc_GetTstConfig ((long) courseCode)) != SOAP_OK) return ReturnCode; if (Gbl.Test.Config.Pluggable == Tst_PLUGGABLE_YES) { /***** Get tags *****/ if ((ReturnCode = Svc_GetTstTags ((long) courseCode,getTestsOut)) != SOAP_OK) return ReturnCode; /***** Get questions *****/ if ((ReturnCode = Svc_GetTstQuestions ((long) courseCode,beginTime,getTestsOut)) != SOAP_OK) return ReturnCode; /***** Get answers *****/ if ((ReturnCode = Svc_GetTstAnswers ((long) courseCode,beginTime,getTestsOut)) != SOAP_OK) return ReturnCode; /***** Get tags for each question *****/ if ((ReturnCode = Svc_GetTstQuestionTags ((long) courseCode,beginTime,getTestsOut)) != SOAP_OK) return ReturnCode; } return SOAP_OK; } /*****************************************************************************/ /**** Get test tags (only not hidden) from database giving a course code *****/ /*****************************************************************************/ static int Svc_GetTstTags (long CrsCod,struct swad__getTestsOutput *getTestsOut) { extern const char *Lan_STR_LANG_ID[1 + Lan_NUM_LANGUAGES]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow; unsigned NumRows; /***** Get available tags from database *****/ NumRows = DB_QuerySELECT (&mysql_res,"can not get test tags", "SELECT TagCod,TagTxt" " FROM tst_tags" " WHERE CrsCod=%ld AND TagHidden='N'" " ORDER BY TagTxt", CrsCod); getTestsOut->tagsArray.__size = (int) NumRows; if (NumRows == 0) getTestsOut->tagsArray.__ptr = NULL; else // Tags found { getTestsOut->tagsArray.__ptr = soap_malloc (Gbl.soap,(getTestsOut->tagsArray.__size) * sizeof (*(getTestsOut->tagsArray.__ptr))); for (NumRow = 0; NumRow < NumRows; NumRow++) { /* Get next tag */ row = mysql_fetch_row (mysql_res); /* Get tag code (row[0]) */ getTestsOut->tagsArray.__ptr[NumRow].tagCode = (int) Str_ConvertStrCodToLongCod (row[0]); /* Get tag text (row[1]) */ getTestsOut->tagsArray.__ptr[NumRow].tagText = (char *) soap_malloc (Gbl.soap,Tst_MAX_BYTES_TAG + 1); Str_Copy (getTestsOut->tagsArray.__ptr[NumRow].tagText,row[1], Tst_MAX_BYTES_TAG); } } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); return SOAP_OK; } /*****************************************************************************/ /******* Get recent test questions from database giving a course code ********/ /*****************************************************************************/ static int Svc_GetTstQuestions (long CrsCod,long BeginTime,struct swad__getTestsOutput *getTestsOut) { extern const char *Tst_StrAnswerTypesXML[Tst_NUM_ANS_TYPES]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow; unsigned NumRows; Tst_AnswerType_t AnswerType; /***** Get recent test questions from database *****/ // DISTINCTROW is necessary to not repeat questions NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get test questions", "SELECT DISTINCTROW tst_questions.QstCod," "tst_questions.AnsType,tst_questions.Shuffle," "tst_questions.Stem,tst_questions.Feedback" " FROM tst_questions,tst_question_tags,tst_tags" " WHERE tst_questions.CrsCod=%ld" " AND tst_questions.QstCod NOT IN" " (SELECT tst_question_tags.QstCod FROM tst_tags,tst_question_tags" " WHERE tst_tags.CrsCod=%ld AND tst_tags.TagHidden='Y'" " AND tst_tags.TagCod=tst_question_tags.TagCod)" " AND tst_questions.QstCod=tst_question_tags.QstCod" " AND tst_question_tags.TagCod=tst_tags.TagCod" " AND tst_tags.CrsCod=%ld" " AND " "(" "tst_questions.EditTime>=FROM_UNIXTIME(%ld)" " OR " "tst_tags.ChangeTime>=FROM_UNIXTIME(%ld)" ")" " ORDER BY QstCod", CrsCod,CrsCod,CrsCod, BeginTime, BeginTime); getTestsOut->questionsArray.__size = (int) NumRows; if (NumRows == 0) getTestsOut->questionsArray.__ptr = NULL; else // Questions found { getTestsOut->questionsArray.__ptr = soap_malloc (Gbl.soap,(getTestsOut->questionsArray.__size) * sizeof (*(getTestsOut->questionsArray.__ptr))); for (NumRow = 0; NumRow < NumRows; NumRow++) { /* Get next question */ row = mysql_fetch_row (mysql_res); /* Get question code (row[0]) */ getTestsOut->questionsArray.__ptr[NumRow].questionCode = (int) Str_ConvertStrCodToLongCod (row[0]); /* Get answer type (row[1]) */ AnswerType = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]); getTestsOut->questionsArray.__ptr[NumRow].answerType = (char *) soap_malloc (Gbl.soap,Tst_MAX_BYTES_ANSWER_TYPE + 1); Str_Copy (getTestsOut->questionsArray.__ptr[NumRow].answerType, Tst_StrAnswerTypesXML[AnswerType], Tst_MAX_BYTES_ANSWER_TYPE); /* Get shuffle (row[2]) */ getTestsOut->questionsArray.__ptr[NumRow].shuffle = (row[2][0] == 'Y') ? 1 : 0; /* Get question stem (row[3]) */ getTestsOut->questionsArray.__ptr[NumRow].stem = (char *) soap_malloc (Gbl.soap,Cns_MAX_BYTES_TEXT + 1); Str_Copy (getTestsOut->questionsArray.__ptr[NumRow].stem,row[3], Cns_MAX_BYTES_TEXT); /* Get question feedback (row[4]) */ getTestsOut->questionsArray.__ptr[NumRow].feedback = (char *) soap_malloc (Gbl.soap,Cns_MAX_BYTES_TEXT + 1); Str_Copy (getTestsOut->questionsArray.__ptr[NumRow].feedback,row[4], Cns_MAX_BYTES_TEXT); } } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); return SOAP_OK; } /*****************************************************************************/ /** Get answers of recent test questions from database giving a course code **/ /*****************************************************************************/ static int Svc_GetTstAnswers (long CrsCod,long BeginTime,struct swad__getTestsOutput *getTestsOut) { extern const char *Tst_StrAnswerTypesXML[Tst_NUM_ANS_TYPES]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow,NumRows; unsigned Index; /***** Get recent test questions from database *****/ NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get test answers", "SELECT QstCod,AnsInd,Correct,Answer,Feedback" " FROM tst_answers WHERE QstCod IN " "(SELECT tst_questions.QstCod" " FROM tst_questions,tst_question_tags,tst_tags" " WHERE tst_questions.CrsCod=%ld" " AND tst_questions.QstCod NOT IN" " (SELECT tst_question_tags.QstCod FROM tst_tags,tst_question_tags" " WHERE tst_tags.CrsCod=%ld AND tst_tags.TagHidden='Y'" " AND tst_tags.TagCod=tst_question_tags.TagCod)" " AND tst_questions.QstCod=tst_question_tags.QstCod" " AND tst_question_tags.TagCod=tst_tags.TagCod" " AND tst_tags.CrsCod=%ld" " AND " "(" "tst_questions.EditTime>=FROM_UNIXTIME(%ld)" " OR " "tst_tags.ChangeTime>=FROM_UNIXTIME(%ld)" ")" ")" " ORDER BY QstCod,AnsInd", CrsCod,CrsCod,CrsCod, BeginTime, BeginTime); getTestsOut->answersArray.__size = (int) NumRows; if (NumRows == 0) getTestsOut->answersArray.__ptr = NULL; else // Answers found { getTestsOut->answersArray.__ptr = soap_malloc (Gbl.soap,(getTestsOut->answersArray.__size) * sizeof (*(getTestsOut->answersArray.__ptr))); for (NumRow = 0; NumRow < NumRows; NumRow++) { /* Get next question */ row = mysql_fetch_row (mysql_res); /* Get question code (row[0]) */ getTestsOut->answersArray.__ptr[NumRow].questionCode = (int) Str_ConvertStrCodToLongCod (row[0]); /* Get answer index (row[1]) */ if (sscanf (row[1],"%u",&Index) == 1) getTestsOut->answersArray.__ptr[NumRow].answerIndex = (int) Index; else getTestsOut->answersArray.__ptr[NumRow].answerIndex = 0; // error /* Get correct (row[2]) */ getTestsOut->answersArray.__ptr[NumRow].correct = (row[2][0] == 'Y') ? 1 : 0; /* Get answer (row[3]) */ getTestsOut->answersArray.__ptr[NumRow].answerText = (char *) soap_malloc (Gbl.soap,Cns_MAX_BYTES_TEXT + 1); Str_Copy (getTestsOut->answersArray.__ptr[NumRow].answerText,row[3], Cns_MAX_BYTES_TEXT); /* Get feedback (row[4]) */ getTestsOut->answersArray.__ptr[NumRow].answerFeedback = (char *) soap_malloc (Gbl.soap,Cns_MAX_BYTES_TEXT + 1); Str_Copy (getTestsOut->answersArray.__ptr[NumRow].answerFeedback,row[4], Cns_MAX_BYTES_TEXT); } } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); return SOAP_OK; } /*****************************************************************************/ /*** Get tags of recent test questions from database giving a course code ****/ /*****************************************************************************/ static int Svc_GetTstQuestionTags (long CrsCod,long BeginTime,struct swad__getTestsOutput *getTestsOut) { extern const char *Tst_StrAnswerTypesXML[Tst_NUM_ANS_TYPES]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow,NumRows; unsigned Index; /***** Get recent test questions from database *****/ NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get test question tags", "SELECT QstCod,TagCod,TagInd" " FROM tst_question_tags WHERE QstCod IN " "(SELECT tst_questions.QstCod" " FROM tst_questions,tst_question_tags,tst_tags" " WHERE tst_questions.CrsCod=%ld" " AND tst_questions.QstCod NOT IN" " (SELECT tst_question_tags.QstCod FROM tst_tags,tst_question_tags" " WHERE tst_tags.CrsCod=%ld AND tst_tags.TagHidden='Y'" " AND tst_tags.TagCod=tst_question_tags.TagCod)" " AND tst_questions.QstCod=tst_question_tags.QstCod" " AND tst_question_tags.TagCod=tst_tags.TagCod" " AND tst_tags.CrsCod=%ld" " AND " "(" "tst_questions.EditTime>=FROM_UNIXTIME(%ld)" " OR " "tst_tags.ChangeTime>=FROM_UNIXTIME(%ld)" ")" ")" " ORDER BY QstCod,TagInd", CrsCod,CrsCod,CrsCod, BeginTime, BeginTime); getTestsOut->questionTagsArray.__size = (int) NumRows; if (NumRows == 0) getTestsOut->questionTagsArray.__ptr = NULL; else // Answers found { getTestsOut->questionTagsArray.__ptr = soap_malloc (Gbl.soap,(getTestsOut->questionTagsArray.__size) * sizeof (*(getTestsOut->questionTagsArray.__ptr))); for (NumRow = 0; NumRow < NumRows; NumRow++) { /* Get next question */ row = mysql_fetch_row (mysql_res); /* Get question code (row[0]) */ getTestsOut->questionTagsArray.__ptr[NumRow].questionCode = (int) Str_ConvertStrCodToLongCod (row[0]); /* Get tag code (row[1]) */ getTestsOut->questionTagsArray.__ptr[NumRow].tagCode = (int) Str_ConvertStrCodToLongCod (row[1]); /* Get tag index (row[2]) */ if (sscanf (row[2],"%u",&Index) == 1) getTestsOut->questionTagsArray.__ptr[NumRow].tagIndex = (int) Index; else getTestsOut->questionTagsArray.__ptr[NumRow].tagIndex = 0; // error } } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); return SOAP_OK; } /*****************************************************************************/ /***************** Return one test question for Trivial game *****************/ /*****************************************************************************/ #define Svc_MAX_BYTES_DEGREES_STR (1024 - 1) int swad__getTrivialQuestion (struct soap *soap, char *wsKey,char *degrees,float lowerScore,float upperScore, // input struct swad__getTrivialQuestionOutput *getTrivialQuestionOut) // output { extern const char *Tst_StrAnswerTypesXML[Tst_NUM_ANS_TYPES]; int ReturnCode; const char *Ptr; char LongStr[1 + 10 + 1]; char DegreesStr[Svc_MAX_BYTES_DEGREES_STR + 1]; char DegStr[ 1 + 1 + 1 + 10 + 1 + 1]; // DegStr=", ' - number ' \0" long DegCod; bool FirstDegree = true; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow; unsigned NumRows; long QstCod = -1L; Tst_AnswerType_t AnswerType; unsigned Index; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_getTrivialQuestion; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.Hierarchy.Crs.CrsCod)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Loop over recipients' nicknames building query *****/ DegreesStr[0] = '\0'; Ptr = degrees; while (*Ptr) { /* Find next string in text until comma (leading and trailing spaces are removed) */ Str_GetNextStringUntilComma (&Ptr,LongStr,1 + 10); /* Check if degree code from string is a valid code */ if (sscanf (LongStr,"%ld",&DegCod) == 1) // Degree code if (DegCod > 0) { /* Add this degree to query */ if (FirstDegree) { snprintf (DegreesStr,sizeof (DegreesStr), "%ld", DegCod); FirstDegree = false; } else { snprintf (DegStr,sizeof (DegStr), ",%ld", DegCod); Str_Concat (DegreesStr,DegStr, Svc_MAX_BYTES_DEGREES_STR); } } } if (!DegreesStr[0]) // Degrees not found return soap_sender_fault (Gbl.soap, "Bad list of degrees codes", "Degrees codes must be integers greater than 0 separated by commas"); /***** Check lowerScore and upperScore *****/ if (lowerScore < -1.0 || lowerScore > 1.0 || upperScore < -1.0 || upperScore > 1.0 || upperScore < lowerScore) return soap_sender_fault (Gbl.soap, "Bad score interval", "lowerScore or upperScore values not valid"); /***** Start query *****/ Str_SetDecimalPointToUS (); // To print the floating point as a dot NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get test questions", "SELECT DISTINCTROW tst_questions.QstCod," "tst_questions.AnsType,tst_questions.Shuffle," "tst_questions.Stem,tst_questions.Feedback," "tst_questions.Score/tst_questions.NumHits AS S" " FROM courses,tst_questions" " WHERE courses.DegCod IN (%s)" " AND courses.CrsCod=tst_questions.CrsCod" " AND tst_questions.AnsType='unique_choice'" " AND tst_questions.NumHits>0" " AND tst_questions.QstCod NOT IN" " (SELECT tst_question_tags.QstCod" " FROM courses,tst_tags,tst_question_tags" " WHERE courses.DegCod IN (%s)" " AND courses.CrsCod=tst_tags.CrsCod" " AND tst_tags.TagHidden='Y'" " AND tst_tags.TagCod=tst_question_tags.TagCod)" " HAVING S>='%f' AND S<='%f'" " ORDER BY RAND(NOW()) LIMIT 1", DegreesStr,DegreesStr, lowerScore,upperScore); Str_SetDecimalPointToLocal (); // Return to local system if (NumRows == 1) // Question found { /* Get next question */ row = mysql_fetch_row (mysql_res); /* Get question code (row[0]) */ QstCod = Str_ConvertStrCodToLongCod (row[0]); getTrivialQuestionOut->question.questionCode = (int) QstCod; /* Get answer type (row[1]) */ AnswerType = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]); getTrivialQuestionOut->question.answerType = (char *) soap_malloc (Gbl.soap,Tst_MAX_BYTES_ANSWER_TYPE + 1); Str_Copy (getTrivialQuestionOut->question.answerType, Tst_StrAnswerTypesXML[AnswerType], Tst_MAX_BYTES_ANSWER_TYPE); /* Get shuffle (row[2]) */ getTrivialQuestionOut->question.shuffle = (row[2][0] == 'Y') ? 1 : 0; /* Get question stem (row[3]) */ getTrivialQuestionOut->question.stem = (char *) soap_malloc (Gbl.soap,Cns_MAX_BYTES_TEXT + 1); Str_Copy (getTrivialQuestionOut->question.stem,row[3], Cns_MAX_BYTES_TEXT); /* Get question feedback (row[4]) */ getTrivialQuestionOut->question.feedback = (char *) soap_malloc (Gbl.soap,Cns_MAX_BYTES_TEXT + 1); Str_Copy (getTrivialQuestionOut->question.feedback,row[4], Cns_MAX_BYTES_TEXT); } else // Empty question { /* Question code (row[0]) */ QstCod = -1L; getTrivialQuestionOut->question.questionCode = -1; /* Answer type (row[1]) */ getTrivialQuestionOut->question.answerType = (char *) soap_malloc (Gbl.soap,1); getTrivialQuestionOut->question.answerType[0] = '\0'; /* Shuffle (row[2]) */ getTrivialQuestionOut->question.shuffle = 0; /* Question stem (row[3]) */ getTrivialQuestionOut->question.stem = (char *) soap_malloc (Gbl.soap,1); getTrivialQuestionOut->question.stem[0] = '\0'; /* Get question feedback (row[4]) */ getTrivialQuestionOut->question.feedback = (char *) soap_malloc (Gbl.soap,1); getTrivialQuestionOut->question.feedback[0] = '\0'; } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); if (QstCod > 0) { /***** Get answer from database *****/ NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get test answers", "SELECT QstCod,AnsInd,Correct,Answer,Feedback" " FROM tst_answers WHERE QstCod=%ld" " ORDER BY AnsInd", QstCod); getTrivialQuestionOut->answersArray.__size = (int) NumRows; if (NumRows == 0) getTrivialQuestionOut->answersArray.__ptr = NULL; else // Answers found { getTrivialQuestionOut->answersArray.__ptr = soap_malloc (Gbl.soap,(getTrivialQuestionOut->answersArray.__size) * sizeof (*(getTrivialQuestionOut->answersArray.__ptr))); for (NumRow = 0; NumRow < NumRows; NumRow++) { /* Get next question */ row = mysql_fetch_row (mysql_res); /* Get question code (row[0]) */ getTrivialQuestionOut->answersArray.__ptr[NumRow].questionCode = (int) Str_ConvertStrCodToLongCod (row[0]); /* Get answer index (row[1]) */ if (sscanf (row[1],"%u",&Index) == 1) getTrivialQuestionOut->answersArray.__ptr[NumRow].answerIndex = (int) Index; else getTrivialQuestionOut->answersArray.__ptr[NumRow].answerIndex = 0; // error /* Get correct (row[2]) */ getTrivialQuestionOut->answersArray.__ptr[NumRow].correct = (row[2][0] == 'Y') ? 1 : 0; /* Get answer (row[3]) */ getTrivialQuestionOut->answersArray.__ptr[NumRow].answerText = (char *) soap_malloc (Gbl.soap,Cns_MAX_BYTES_TEXT + 1); Str_Copy (getTrivialQuestionOut->answersArray.__ptr[NumRow].answerText,row[3], Cns_MAX_BYTES_TEXT); /* Get feedback (row[4]) */ getTrivialQuestionOut->answersArray.__ptr[NumRow].answerFeedback = (char *) soap_malloc (Gbl.soap,Cns_MAX_BYTES_TEXT + 1); Str_Copy (getTrivialQuestionOut->answersArray.__ptr[NumRow].answerFeedback,row[4], Cns_MAX_BYTES_TEXT); } } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); } return SOAP_OK; } /*****************************************************************************/ /*************** Get a directory tree in a course or a group *****************/ /*****************************************************************************/ // TODO: should treeCode be a string with one of the followins values? // documents // shared // marks int swad__getDirectoryTree (struct soap *soap, char *wsKey,int courseCode,int groupCode,int treeCode, // input struct swad__getDirectoryTreeOutput *getDirectoryTreeOut) // output { extern const char *Brw_RootFolderInternalNames[Brw_NUM_TYPES_FILE_BROWSER]; int ReturnCode; char XMLFileName[PATH_MAX + 1]; unsigned long FileSize; unsigned long NumBytesRead; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_getDirectoryTree; Gbl.Hierarchy.Crs.CrsCod = (long) courseCode; Gbl.Crs.Grps.GrpCod = (long) groupCode; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.Hierarchy.Crs.CrsCod)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Check course and group codes *****/ if ((ReturnCode = Svc_CheckCourseAndGroupCodes (Gbl.Hierarchy.Crs.CrsCod,Gbl.Crs.Grps.GrpCod)) != SOAP_OK) return ReturnCode; /***** Check if I am a student, non-editing teacher or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_STD && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_NET && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_TCH) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to course"); /***** Check if I belong to course/group *****/ if (Gbl.Crs.Grps.GrpCod > 0) if (!Grp_GetIfIBelongToGrp (Gbl.Crs.Grps.GrpCod)) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to group"); /***** Check if tree code is correct *****/ if (treeCode <= 0 || treeCode > 3) return soap_sender_fault (Gbl.soap, "Bad tree code", "Tree code must be 1 (documents), 2 (shared files) or 3 (marks)"); /***** Return directory tree *****/ /* Get directory tree into XML file */ if (courseCode > 0) { if (groupCode > 0) switch (treeCode) { case 1: // Documents Gbl.FileBrowser.Type = Brw_SHOW_DOC_GRP; break; case 2: // Shared files Gbl.FileBrowser.Type = Brw_ADMI_SHR_GRP; break; case 3: // Marks Gbl.FileBrowser.Type = Brw_SHOW_MRK_GRP; break; } else // groupCode <= 0 switch (treeCode) { case 1: // Documents Gbl.FileBrowser.Type = Brw_SHOW_DOC_CRS; break; case 2: // Shared files Gbl.FileBrowser.Type = Brw_ADMI_SHR_CRS; break; case 3: // Marks Gbl.FileBrowser.Type = Brw_SHOW_MRK_CRS; break; } } else return soap_sender_fault (Gbl.soap, "Bad course code", "Course code must be a integer greater than 0"); /* Initialize path to private directory */ Gbl.Hierarchy.Crs.CrsCod = (courseCode > 0) ? (long) courseCode : -1L; Gbl.Crs.Grps.GrpCod = (groupCode > 0) ? (long) groupCode : -1L; snprintf (Gbl.Crs.PathPriv,sizeof (Gbl.Crs.PathPriv), "%s/%ld", Cfg_PATH_CRS_PRIVATE,Gbl.Hierarchy.Crs.CrsCod); Brw_InitializeFileBrowser (); Brw_SetFullPathInTree (Brw_RootFolderInternalNames[Gbl.FileBrowser.Type],"."); /* Check if exists the directory for HTML output. If not exists, create it */ Fil_CreateDirIfNotExists (Cfg_PATH_OUT_PRIVATE); /* Create a unique name for the file */ snprintf (XMLFileName,sizeof (XMLFileName), "%s/%s.xml", Cfg_PATH_OUT_PRIVATE,Gbl.UniqueNameEncrypted); /* Open file for writing and reading */ if ((Gbl.F.XML = fopen (XMLFileName,"w+t")) == NULL) return soap_receiver_fault (Gbl.soap, "Can not get tree", "Can not create temporary XML file"); /* Get directory tree into XML file */ XML_WriteStartFile (Gbl.F.XML,"tree",false); if (!Brw_CheckIfFileOrFolderIsSetAsHiddenInDB (Brw_IS_FOLDER, Gbl.FileBrowser.Priv.FullPathInTree)) // If root folder is visible Svc_ListDir (1,Gbl.FileBrowser.Priv.PathRootFolder,Brw_RootFolderInternalNames[Gbl.FileBrowser.Type]); XML_WriteEndFile (Gbl.F.XML,"tree"); /* Compute file size */ // fseek (Gbl.F.XML,0L,SEEK_END); FileSize = (unsigned long) ftell (Gbl.F.XML); fseek (Gbl.F.XML,0L,SEEK_SET); /* Copy XML content from file to memory */ getDirectoryTreeOut->tree = (char *) soap_malloc (Gbl.soap,FileSize + 1); NumBytesRead = fread (getDirectoryTreeOut->tree,1,FileSize,Gbl.F.XML); getDirectoryTreeOut->tree[NumBytesRead] = '\0'; /* Close and remove XML file */ Fil_CloseXMLFile (); unlink (XMLFileName); return SOAP_OK; } /*****************************************************************************/ /************************ List a directory recursively ***********************/ /*****************************************************************************/ static void Svc_ListDir (unsigned Level,const char *Path,const char *PathInTree) { extern const char *Txt_NEW_LINE; struct dirent **FileList; int NumFile; int NumFiles; char PathFileRel[PATH_MAX + 1 + NAME_MAX + 1]; char PathFileInExplTree[PATH_MAX + 1 + NAME_MAX + 1]; struct stat FileStatus; /***** Scan directory *****/ if ((NumFiles = scandir (Path,&FileList,NULL,alphasort)) >= 0) // No error { /***** List files *****/ for (NumFile = 0; NumFile < NumFiles; NumFile++) { if (strcmp (FileList[NumFile]->d_name,".") && strcmp (FileList[NumFile]->d_name,"..")) // Skip directories "." and ".." { snprintf (PathFileRel,sizeof (PathFileRel), "%s/%s", Path,FileList[NumFile]->d_name); snprintf (PathFileInExplTree,sizeof (PathFileInExplTree), "%s/%s", PathInTree,FileList[NumFile]->d_name); if (!lstat (PathFileRel,&FileStatus)) // On success ==> 0 is returned { /***** Construct the full path of the file or folder *****/ Brw_SetFullPathInTree (PathInTree,FileList[NumFile]->d_name); if (S_ISDIR (FileStatus.st_mode)) // It's a directory { /***** Write a row for the subdirectory *****/ if (Svc_WriteRowFileBrowser (Level,Brw_IS_FOLDER,FileList[NumFile]->d_name)) { /* List subtree starting at this this directory */ Svc_ListDir (Level + 1,PathFileRel,PathFileInExplTree); /* Indent and end dir */ Svc_IndentXMLLine (Level); fprintf (Gbl.F.XML,"%s",Txt_NEW_LINE); } } else if (S_ISREG (FileStatus.st_mode)) // It's a regular file Svc_WriteRowFileBrowser (Level, Str_FileIs (FileList[NumFile]->d_name,"url") ? Brw_IS_LINK : Brw_IS_FILE, FileList[NumFile]->d_name); } } free ((void *) FileList[NumFile]); } free ((void *) FileList); } } /*****************************************************************************/ /*********************** Write a row of a file browser ***********************/ /*****************************************************************************/ // If it is the first row (root folder), always show it // If it is not the first row, it is shown or not depending on whether it is hidden or not // If the row is visible, return true. If it is hidden, return false static bool Svc_WriteRowFileBrowser (unsigned Level,Brw_FileType_t FileType,const char *FileName) { extern const char *Txt_NEW_LINE; extern const char *Txt_LICENSES[Brw_NUM_LICENSES]; struct FileMetadata FileMetadata; char PhotoURL[Cns_MAX_BYTES_WWW + 1]; /***** Is this row hidden or visible? *****/ if (Gbl.FileBrowser.Type == Brw_SHOW_DOC_CRS || Gbl.FileBrowser.Type == Brw_SHOW_DOC_GRP) if (Brw_CheckIfFileOrFolderIsSetAsHiddenInDB (FileType, Gbl.FileBrowser.Priv.FullPathInTree)) return false; /***** XML row *****/ /* Indent */ Svc_IndentXMLLine (Level); /* Write file or folder data */ if (FileType == Brw_IS_FOLDER) fprintf (Gbl.F.XML,"%s", FileName,Txt_NEW_LINE); else // File or link { /* Get file metadata */ Brw_GetFileMetadataByPath (&FileMetadata); Brw_GetFileTypeSizeAndDate (&FileMetadata); if (FileMetadata.FilCod <= 0) // No entry for this file in database table of files /* Add entry to the table of files/folders */ FileMetadata.FilCod = Brw_AddPathToDB (-1L,FileMetadata.FileType, Gbl.FileBrowser.Priv.FullPathInTree,false,Brw_LICENSE_DEFAULT); Gbl.Usrs.Other.UsrDat.UsrCod = FileMetadata.PublisherUsrCod; Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,Usr_DONT_GET_PREFS); Pho_BuildLinkToPhoto (&Gbl.Usrs.Me.UsrDat,PhotoURL); fprintf (Gbl.F.XML,"" "%ld" "%lu" "" "%s" "%s" "%s" "%s", FileName, FileMetadata.FilCod, (unsigned long) FileMetadata.Size, (unsigned long) FileMetadata.Time, Txt_LICENSES[FileMetadata.License], Gbl.Usrs.Other.UsrDat.FullName, PhotoURL, Txt_NEW_LINE); } return true; } /*****************************************************************************/ /******************************* Indent XML line *****************************/ /*****************************************************************************/ static void Svc_IndentXMLLine (unsigned Level) { for ( ; Level; Level--) fprintf (Gbl.F.XML,"\t"); } /*****************************************************************************/ /**************************** Get info of a file *****************************/ /*****************************************************************************/ int swad__getFile (struct soap *soap, char *wsKey,int fileCode, // input struct swad__getFileOutput *getFileOut) // output { extern const char *Txt_LICENSES[Brw_NUM_LICENSES]; int ReturnCode; struct FileMetadata FileMetadata; char URL[Cns_MAX_BYTES_WWW + 1]; char PhotoURL[Cns_MAX_BYTES_WWW + 1]; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_getFile; /***** Allocate space for strings *****/ getFileOut->fileName = (char *) soap_malloc (Gbl.soap,NAME_MAX + 1); getFileOut->URL = (char *) soap_malloc (Gbl.soap,Cns_MAX_BYTES_WWW + 1); getFileOut->license = (char *) soap_malloc (Gbl.soap,Brw_MAX_BYTES_LICENSE + 1); getFileOut->publisherName = (char *) soap_malloc (Gbl.soap,Usr_MAX_BYTES_FULL_NAME + 1); getFileOut->publisherPhoto = (char *) soap_malloc (Gbl.soap,Cns_MAX_BYTES_WWW + 1); /***** Default values returned on error *****/ getFileOut->fileName[0] = '\0'; getFileOut->URL[0] = '\0'; getFileOut->size = 0; getFileOut->time = 0; getFileOut->license[0] = '\0'; getFileOut->publisherName[0] = '\0'; getFileOut->publisherPhoto[0] = '\0'; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /***** Get file metadata *****/ FileMetadata.FilCod = (long) fileCode; if (FileMetadata.FilCod <= 0) return soap_sender_fault (Gbl.soap, "Bad file code", "File code must be a integer greater than 0"); Brw_GetFileMetadataByCod (&FileMetadata); if (FileMetadata.FilCod <= 0) return soap_sender_fault (Gbl.soap, "File does not exist, please refresh", "The file requested does not exists"); /***** Set course and group codes *****/ Brw_GetCrsGrpFromFileMetadata (FileMetadata.FileBrowser,FileMetadata.Cod, &Gbl.Hierarchy.Ins.InsCod, &Gbl.Hierarchy.Ctr.CtrCod, &Gbl.Hierarchy.Deg.DegCod, &Gbl.Hierarchy.Crs.CrsCod, &Gbl.Crs.Grps.GrpCod); /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.Hierarchy.Crs.CrsCod)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Check course and group codes *****/ if ((ReturnCode = Svc_CheckCourseAndGroupCodes (Gbl.Hierarchy.Crs.CrsCod,Gbl.Crs.Grps.GrpCod)) != SOAP_OK) return ReturnCode; /***** Check if I am a student, non-editing teacher or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_STD && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_NET && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_TCH) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to course"); /***** Check if I belong to group *****/ if (Gbl.Crs.Grps.GrpCod > 0) if (!Grp_GetIfIBelongToGrp (Gbl.Crs.Grps.GrpCod)) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to group"); /***** Check if file is in a valid zone *****/ switch ((Gbl.FileBrowser.Type = FileMetadata.FileBrowser)) { case Brw_ADMI_DOC_CRS: case Brw_ADMI_DOC_GRP: case Brw_ADMI_SHR_CRS: case Brw_ADMI_SHR_GRP: break; case Brw_ADMI_MRK_CRS: case Brw_ADMI_MRK_GRP: // Downloading a file of marks is only allowed for teachers if (Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_TCH) return soap_receiver_fault (Gbl.soap, "Wrong tree", "Wrong file zone"); break; default: return soap_sender_fault (Gbl.soap, "Wrong tree", "Wrong file zone"); } /***** Set paths *****/ Hie_InitHierarchy (); Brw_SetFullPathInTree (FileMetadata.PathInTreeUntilFilFolLnk, FileMetadata.FilFolLnkName); Brw_InitializeFileBrowser (); /***** Get file size and date *****/ Brw_GetFileTypeSizeAndDate (&FileMetadata); /***** Update number of views *****/ Brw_GetAndUpdateFileViews (&FileMetadata); /***** Create and get link to download the file *****/ Brw_GetLinkToDownloadFile (FileMetadata.PathInTreeUntilFilFolLnk, FileMetadata.FilFolLnkName, URL); /***** Copy data into output structure *****/ Str_Copy (getFileOut->fileName,FileMetadata.FilFolLnkName, NAME_MAX); Str_Copy (getFileOut->URL,URL, Cns_MAX_BYTES_WWW); getFileOut->size = (int) FileMetadata.Size; getFileOut->time = (int) FileMetadata.Time; Str_Copy (getFileOut->license,Txt_LICENSES[FileMetadata.License], Brw_MAX_BYTES_LICENSE); if ((Gbl.Usrs.Other.UsrDat.UsrCod = FileMetadata.PublisherUsrCod) > 0) /* Get publisher's data */ if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,Usr_DONT_GET_PREFS)) /* Copy publisher's data into output structure */ { Str_Copy (getFileOut->publisherName,Gbl.Usrs.Other.UsrDat.FullName, Usr_MAX_BYTES_FULL_NAME); Pho_BuildLinkToPhoto (&Gbl.Usrs.Other.UsrDat,PhotoURL); Str_Copy (getFileOut->publisherPhoto,PhotoURL, Cns_MAX_BYTES_WWW); } return SOAP_OK; } /*****************************************************************************/ /**************************** Get info of my marks ***************************/ /*****************************************************************************/ int swad__getMarks (struct soap *soap, char *wsKey,int fileCode, // input struct swad__getMarksOutput *getMarksOut) // output { int ReturnCode; struct FileMetadata FileMetadata; char SummaryStr[Ntf_MAX_BYTES_SUMMARY + 1]; // Not used char *ContentStr; size_t Length; /***** Initializations *****/ Gbl.soap = soap; Gbl.WebService.Function = Svc_getMarks; getMarksOut->content = NULL; /***** Check web service key *****/ if ((ReturnCode = Svc_CheckWSKey (wsKey)) != SOAP_OK) return ReturnCode; if (Gbl.Usrs.Me.UsrDat.UsrCod < 0) // Web service key does not exist in database return soap_receiver_fault (Gbl.soap, "Bad web service key", "Web service key does not exist in database"); /***** Get file metadata *****/ FileMetadata.FilCod = (long) fileCode; Brw_GetFileMetadataByCod (&FileMetadata); if (FileMetadata.FileType != Brw_IS_FILE || FileMetadata.IsHidden || (FileMetadata.FileBrowser != Brw_ADMI_MRK_CRS && FileMetadata.FileBrowser != Brw_ADMI_MRK_GRP)) return soap_receiver_fault (Gbl.soap, "Bad file code", "You can not get marks from this file"); /***** Set course and group codes *****/ Brw_GetCrsGrpFromFileMetadata (FileMetadata.FileBrowser,FileMetadata.Cod, &Gbl.Hierarchy.Ins.InsCod, &Gbl.Hierarchy.Ctr.CtrCod, &Gbl.Hierarchy.Deg.DegCod, &Gbl.Hierarchy.Crs.CrsCod, &Gbl.Crs.Grps.GrpCod); /***** Check course and group codes *****/ if ((ReturnCode = Svc_CheckCourseAndGroupCodes (Gbl.Hierarchy.Crs.CrsCod,Gbl.Crs.Grps.GrpCod)) != SOAP_OK) return ReturnCode; /***** Get some of my data *****/ if (!Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.Hierarchy.Crs.CrsCod)) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.Role.Logged = Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role; /***** Check if I am a student, non-editing teacher or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_STD && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_NET && Gbl.Usrs.Me.UsrDat.Roles.InCurrentCrs.Role != Rol_TCH) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to course"); /***** Check if I belong to group *****/ if (Gbl.Crs.Grps.GrpCod > 0) if (!Grp_GetIfIBelongToGrp (Gbl.Crs.Grps.GrpCod)) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to group"); /***** Get content *****/ ContentStr = NULL; Mrk_GetNotifMyMarks (SummaryStr, // Not used &ContentStr, FileMetadata.FilCod,Gbl.Usrs.Me.UsrDat.UsrCod,true); if (ContentStr != NULL) { Length = strlen (ContentStr); getMarksOut->content = (char *) soap_malloc (Gbl.soap,Length + 1); Str_Copy (getMarksOut->content,ContentStr, Length); free ((void *) ContentStr); ContentStr = NULL; } else { getMarksOut->content = (char *) soap_malloc (Gbl.soap,1); getMarksOut->content[0] = '\0'; } return SOAP_OK; }