// 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-2016 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 = swad.o swad_action.o swad_assignment.o swad_file_browser.o swad_chat.o swad_connected.o swad_course.o swad_cryptography.o swad_centre.o swad_date.o swad_database.o swad_degree.o swad_department.o swad_exam.o swad_file.o swad_forum.o swad_group.o swad_holiday.o swad_import.o swad_institution.o swad_layout.o swad_link.o swad_changelog.o swad_message.o swad_notice.o swad_notification.o swad_pagination.o swad_parameter.o swad_place.o swad_plugin.o swad_preference.o swad_record.o swad_session.o swad_statistic.o swad_string.o swad_syllabus.o swad_theme.o swad_test.o swad_timetable.o swad_text.o swad_user.o swad_web_service.o sha2.o 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 setlocale, LC_NUMERIC... #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_ID.h" #include "swad_notice.h" #include "swad_notification.h" #include "swad_password.h" #include "swad_user.h" #include "swad_web_service.h" /*****************************************************************************/ /************** External global variables from others modules ****************/ /*****************************************************************************/ extern struct Globals Gbl; extern const char Str_BIN_TO_BASE64URL[64]; /*****************************************************************************/ /***************************** 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 }; /*****************************************************************************/ /********************************* Data types ********************************/ /*****************************************************************************/ /*****************************************************************************/ /***************************** Private prototypes ****************************/ /*****************************************************************************/ // static int Svc_CheckIfIPIsAllowed (void); static int Svc_GetPlgCodFromAppKey (const char *appKey); static int Svc_CheckIdSession (const char *IdSession); static int Svc_CheckWSKey (char *WSKey); static int Svc_CheckCourseAndGroupCodes (long CrsCod,long GrpCod); static int Svc_GenerateNewWSKey (long UsrCod,char *WSKey); static int Svc_RemoveOldWSKeys (void); static int Svc_GetCurrentDegCodFromCurrentCrsCod (void); static int Svc_GetSomeUsrDataFromUsrCod (struct UsrData *UsrDat,long CrsCod); static int Svc_GetRoleFromInternalRole (Rol_Role_t Role); static int Svc_CheckParamsNewAccount (char *NewNicknameWithArroba, // Input char *NewNicknameWithoutArroba, // Output char *NewEmail, // Input-output char *NewPlainPassword, // Input char *NewEncryptedPassword); // Output static void Svc_CopyUsrData (struct swad__user *Usr,struct UsrData *UsrDat,bool UsrIDIsVisible); static void Svc_GetListGrpsInAttendanceEvent (long AttCod,char **ListGroups); 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 IP of the requester of a web service ***************/ /*********** is one of the IP allowed in the plugins ***************/ /*****************************************************************************/ /* static int Svc_CheckIfIPIsAllowed (void) { char IP[Cns_MAX_LENGTH_IP+1]; char Query[256+Cns_MAX_LENGTH_IP]; ***** Get IP ***** if (getenv ("REMOTE_ADDR")) { strncpy (IP,getenv ("REMOTE_ADDR"),Cns_MAX_LENGTH_IP); IP[Cns_MAX_LENGTH_IP] = '\0'; } else IP[0] = '\0'; ***** Get number of plugins with a IP address ***** sprintf (Query,"SELECT COUNT(*) FROM plugins WHERE IP='%s'",IP); if (!DB_QueryCOUNT (Query,"can not check IP")) { sprintf (Gbl.Message,"Sender's IP (%s) is forbidden",IP); return soap_sender_fault (Gbl.soap, Gbl.Message, "The IP of the requester of a web service is not allowed"); } return SOAP_OK; } */ /*****************************************************************************/ /****** 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) { char Query[256+Plg_MAX_LENGTH_PLUGIN_APP_KEY]; MYSQL_RES *mysql_res; MYSQL_ROW row; Gbl.WebService.PlgCod = -1L; /***** Get number of plugins with a IP address *****/ sprintf (Query,"SELECT PlgCod FROM plugins WHERE AppKey='%s'",appKey); if (DB_QuerySELECT (Query,&mysql_res,"can not check application key")) // 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; char Query[512]; /***** 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) != Ses_LENGTH_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 *****/ sprintf (Query,"SELECT COUNT(*) FROM sessions WHERE SessionId='%s'", IdSession); if (DB_QueryCOUNT (Query,"can not get session data") != 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) { char Query[512]; 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 *****/ sprintf (Query,"SELECT UsrCod,PlgCod FROM ws_keys WHERE WSKey='%s'", WSKey); if (DB_QuerySELECT (Query,&mysql_res,"can not get existence of key")) // 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) { char Query[512]; /***** 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 *****/ sprintf (Query,"SELECT COUNT(*) FROM courses WHERE CrsCod='%ld'", CrsCod); if (DB_QueryCOUNT (Query,"can not get course") != 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 *****/ sprintf (Query,"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); if (DB_QueryCOUNT (Query,"can not get group") != 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) { int ReturnCode; char Query[512]; /***** Remove expired web service keys *****/ if ((ReturnCode = Svc_RemoveOldWSKeys ()) != SOAP_OK) return ReturnCode; /***** Create a unique name for the key *****/ strcpy (WSKey,Gbl.UniqueNameEncrypted); /***** Check that key does not exist in database *****/ sprintf (Query,"SELECT COUNT(*) FROM ws_keys WHERE WSKey='%s'", WSKey); if (DB_QueryCOUNT (Query,"can not get existence of key")) return soap_receiver_fault (Gbl.soap, "Error when generating key", "Generated key already existed in database"); /***** Insert key into database *****/ sprintf (Query,"INSERT INTO ws_keys (WSKey,UsrCod,PlgCod,LastTime)" " VALUES ('%s','%ld','%ld',NOW())", WSKey,UsrCod,Gbl.WebService.PlgCod); DB_QueryINSERT (Query,"can not insert new key"); 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 must contain an existing user's code static int Svc_GetSomeUsrDataFromUsrCod (struct UsrData *UsrDat,long CrsCod) { char Query[512]; MYSQL_RES *mysql_res; MYSQL_ROW row; /***** Get some user's data *****/ /* Query database */ sprintf (Query,"SELECT Surname1,Surname2,FirstName,Photo,DATE_FORMAT(Birthday,'%%Y%%m%%d')" " FROM usr_data WHERE UsrCod='%ld'", UsrDat->UsrCod); /* Check number of rows in result */ if (DB_QuerySELECT (Query,&mysql_res,"can not get user's data") != 1) return soap_receiver_fault (Gbl.soap, "Can not get user's data from database", "User does not exist in database"); /* Read some user's data */ row = mysql_fetch_row (mysql_res); /* Get user's name */ strncpy (UsrDat->Surname1 ,row[0],sizeof (UsrDat->Surname1 )-1); strncpy (UsrDat->Surname2 ,row[1],sizeof (UsrDat->Surname2 )-1); strncpy (UsrDat->FirstName,row[2],sizeof (UsrDat->FirstName)-1); UsrDat->Surname1 [sizeof (UsrDat->Surname1 )-1] = '\0'; UsrDat->Surname2 [sizeof (UsrDat->Surname2 )-1] = '\0'; UsrDat->FirstName[sizeof (UsrDat->FirstName)-1] = '\0'; /* Get user's photo */ strncpy (UsrDat->Photo,row[3],sizeof (UsrDat->Photo)-1); UsrDat->Photo[sizeof (UsrDat->Photo)-1] = '\0'; /* Get user's brithday */ strncpy (UsrDat->Birthday.YYYYMMDD,row[4],4+2+2); UsrDat->Birthday.YYYYMMDD[4+2+2] = '\0'; /* 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) sprintf (Query,"SELECT Role FROM crs_usr" " WHERE CrsCod='%ld' AND UsrCod='%ld'", CrsCod,UsrDat->UsrCod); else sprintf (Query,"SELECT MAX(Role)" " FROM crs_usr WHERE UsrCod='%ld'", UsrDat->UsrCod); if (DB_QuerySELECT (Query,&mysql_res,"can not get user's role") == 1) { /* Read the maximum role */ row = mysql_fetch_row (mysql_res); if (sscanf (row[0],"%u",&UsrDat->RoleInCurrentCrsDB) != 1) UsrDat->RoleInCurrentCrsDB = Rol_UNKNOWN; } else UsrDat->RoleInCurrentCrsDB = Rol__GUEST_; /* Free structure that stores the query result */ DB_FreeMySQLResult (&mysql_res); return SOAP_OK; } /*****************************************************************************/ /****** Get the role returned via web service from internal role code ********/ /*****************************************************************************/ static int Svc_GetRoleFromInternalRole (Rol_Role_t Role) { switch (Role) { case Rol__GUEST_: case Rol_VISITOR: return 1; // guest or visitor case Rol_STUDENT: return 2; // student case Rol_TEACHER: return 3; // teacher default: return 0; // unknown } return 0; // unknown } /*****************************************************************************/ /**************************** 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_WITH_ARROBA+1]; char NewEncryptedPassword[Cry_LENGTH_ENCRYPTED_STR_SHA512_BASE64+1]; int Result; int ReturnCode; Gbl.soap = soap; Gbl.WebService.Function = Svc_createAccount; /***** Allocate space for strings *****/ createAccountOut->wsKey = (char *) soap_malloc (Gbl.soap,256); /***** 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 *****/ strcpy (Gbl.Usrs.Me.UsrDat.Password,NewEncryptedPassword); /***** User does not exist in the platform, so create him/her! *****/ Acc_CreateNewUsr (&Gbl.Usrs.Me.UsrDat); /***** Save nickname *****/ Nck_UpdateMyNick (NewNicknameWithoutArroba); strcpy (Gbl.Usrs.Me.UsrDat.Nickname,NewNicknameWithoutArroba); /***** Save e-mail *****/ if (Mai_UpdateEmailInDB (&Gbl.Usrs.Me.UsrDat,userEmail)) { /* E-mail updated sucessfully */ strcpy (Gbl.Usrs.Me.UsrDat.Email,userEmail); 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, // Output char *NewEmail, // Input-output char *NewPlainPassword, // Input char *NewEncryptedPassword) // Output { char Query[1024]; /***** Step 1/3: Check new nickname *****/ /* Make a copy without possible starting arrobas */ strncpy (NewNicknameWithoutArroba,NewNicknameWithArroba,Nck_MAX_BYTES_NICKNAME_WITH_ARROBA); NewNicknameWithoutArroba[Nck_MAX_BYTES_NICKNAME_WITH_ARROBA] = '\0'; 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 *****/ sprintf (Query,"SELECT COUNT(*) FROM usr_nicknames WHERE Nickname='%s'", NewNicknameWithoutArroba); if (DB_QueryCOUNT (Query,"can not check if nickname already existed")) // 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 e-mail *****/ if (Mai_CheckIfEmailIsValid (NewEmail)) // New e-mail is valid { /***** Check if the new e-mail matches any of the confirmed e-mails of other users *****/ sprintf (Query,"SELECT COUNT(*) FROM usr_emails" " WHERE E_mail='%s' AND Confirmed='Y'", NewEmail); if (DB_QueryCOUNT (Query,"can not check if e-mail already existed")) // An e-mail of another user is the same that my e-mail return Svc_CHECK_NEW_ACCOUNT_EMAIL_REGISTERED_BY_ANOTHER_USER; } else // New e-mail 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[512]; int ReturnCode; char Query[512]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRows; char PhotoURL[PATH_MAX+1]; Gbl.soap = soap; Gbl.WebService.Function = Svc_loginByUserPasswordKey; /***** Allocate space for strings *****/ loginByUserPasswordKeyOut->wsKey = (char *) soap_malloc (Gbl.soap,256); loginByUserPasswordKeyOut->userNickname = (char *) soap_malloc (Gbl.soap,Nck_MAX_LENGTH_NICKNAME_WITHOUT_ARROBA+1); loginByUserPasswordKeyOut->userID = (char *) soap_malloc (Gbl.soap,256); loginByUserPasswordKeyOut->userFirstname = (char *) soap_malloc (Gbl.soap,256); loginByUserPasswordKeyOut->userSurname1 = (char *) soap_malloc (Gbl.soap,256); loginByUserPasswordKeyOut->userSurname2 = (char *) soap_malloc (Gbl.soap,256); loginByUserPasswordKeyOut->userPhoto = (char *) soap_malloc (Gbl.soap,PATH_MAX+1); loginByUserPasswordKeyOut->userBirthday = (char *) soap_malloc (Gbl.soap,8+2+2+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 e-mail, @nickname or ID are valid *****/ strncpy (UsrIDNickOrEmail,userID,255); UsrIDNickOrEmail[255] = '\0'; if (Nck_CheckIfNickWithArrobaIsValid (UsrIDNickOrEmail)) // 1: It's a nickname { Str_RemoveLeadingArrobas (UsrIDNickOrEmail); /* User has typed a nickname */ sprintf (Query,"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 e-mail { /* User has typed an e-mail */ // TODO: Get only if email confirmed? sprintf (Query,"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 e-mail { // 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? sprintf (Query,"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, e-mail 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 = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get user's data"))) // 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]); if (Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,-1L) == SOAP_OK) // Get some user's data from database { Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; loginByUserPasswordKeyOut->userCode = (int) Gbl.Usrs.Me.UsrDat.UsrCod; strncpy (loginByUserPasswordKeyOut->userNickname,Gbl.Usrs.Me.UsrDat.Nickname,Nck_MAX_LENGTH_NICKNAME_WITHOUT_ARROBA); loginByUserPasswordKeyOut->userNickname[Nck_MAX_LENGTH_NICKNAME_WITHOUT_ARROBA] = '\0'; strncpy (loginByUserPasswordKeyOut->userID,Gbl.Usrs.Me.UsrDat.IDs.List[0].ID,255); // TODO: What user's ID? loginByUserPasswordKeyOut->userID[255] = '\0'; strncpy (loginByUserPasswordKeyOut->userSurname1,Gbl.Usrs.Me.UsrDat.Surname1,255); loginByUserPasswordKeyOut->userSurname1[255] = '\0'; strncpy (loginByUserPasswordKeyOut->userSurname2,Gbl.Usrs.Me.UsrDat.Surname2,255); loginByUserPasswordKeyOut->userSurname2[255] = '\0'; strncpy (loginByUserPasswordKeyOut->userFirstname,Gbl.Usrs.Me.UsrDat.FirstName,255); loginByUserPasswordKeyOut->userFirstname[255] = '\0'; Pho_BuildLinkToPhoto (&Gbl.Usrs.Me.UsrDat,PhotoURL,false); strncpy (loginByUserPasswordKeyOut->userPhoto,PhotoURL,PATH_MAX); loginByUserPasswordKeyOut->userPhoto[PATH_MAX] = '\0'; strncpy (loginByUserPasswordKeyOut->userBirthday,Gbl.Usrs.Me.UsrDat.Birthday.YYYYMMDD,4+2+2); loginByUserPasswordKeyOut->userBirthday[4+2+2] = '\0'; loginByUserPasswordKeyOut->userRole = Svc_GetRoleFromInternalRole (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB); } else { loginByUserPasswordKeyOut->userCode = -1; loginByUserPasswordKeyOut->userID = NULL; loginByUserPasswordKeyOut->userSurname1 = NULL; loginByUserPasswordKeyOut->userSurname2 = NULL; loginByUserPasswordKeyOut->userFirstname = NULL; loginByUserPasswordKeyOut->userPhoto = NULL; loginByUserPasswordKeyOut->userRole = 0; } } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); if (NumRows == 1) /***** Generate a key used in subsequents calls to other web services *****/ return Svc_GenerateNewWSKey ((long) loginByUserPasswordKeyOut->userCode, loginByUserPasswordKeyOut->wsKey); else 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; char Query[512]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRows; char PhotoURL[PATH_MAX+1]; Gbl.soap = soap; Gbl.WebService.Function = Svc_loginBySessionKey; /***** Allocate space for strings *****/ loginBySessionKeyOut->wsKey = (char *) soap_malloc (Gbl.soap,256); loginBySessionKeyOut->userNickname = (char *) soap_malloc (Gbl.soap,Nck_MAX_LENGTH_NICKNAME_WITHOUT_ARROBA+1); loginBySessionKeyOut->userID = (char *) soap_malloc (Gbl.soap,256); loginBySessionKeyOut->userFirstname = (char *) soap_malloc (Gbl.soap,256); loginBySessionKeyOut->userSurname1 = (char *) soap_malloc (Gbl.soap,256); loginBySessionKeyOut->userSurname2 = (char *) soap_malloc (Gbl.soap,256); loginBySessionKeyOut->userPhoto = (char *) soap_malloc (Gbl.soap,PATH_MAX+1); loginBySessionKeyOut->userBirthday = (char *) soap_malloc (Gbl.soap,8+2+2+1); loginBySessionKeyOut->degreeName = (char *) soap_malloc (Gbl.soap,256); loginBySessionKeyOut->courseName = (char *) soap_malloc (Gbl.soap,256); /***** 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 = 0; // unknown loginBySessionKeyOut->degreeName[0] = '\0'; loginBySessionKeyOut->courseName[0] = '\0'; /***** Get plugin code *****/ if ((ReturnCode = Svc_GetPlgCodFromAppKey ((const char *) appKey)) != SOAP_OK) return ReturnCode; /***** Check IP of the sender *****/ // if ((ReturnCode = Svc_CheckIfIPIsAllowed ()) != 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 *****/ sprintf (Query,"SELECT UsrCod,DegCod,CrsCod FROM sessions" " WHERE SessionId='%s'",sessionID); if ((NumRows = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get session data"))) // Session found in table of sessions { row = mysql_fetch_row (mysql_res); /***** Get course (row[2]) *****/ Gbl.CurrentCrs.Crs.CrsCod = Str_ConvertStrCodToLongCod (row[2]); Crs_GetDataOfCourseByCod (&Gbl.CurrentCrs.Crs); loginBySessionKeyOut->courseCode = (int) Gbl.CurrentCrs.Crs.CrsCod; strncpy (loginBySessionKeyOut->courseName,Gbl.CurrentCrs.Crs.FullName,255); loginBySessionKeyOut->courseName[255] = '\0'; /***** Get user code (row[0]) *****/ Gbl.Usrs.Me.UsrDat.UsrCod = Str_ConvertStrCodToLongCod (row[0]); if (Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.CurrentCrs.Crs.CrsCod) == SOAP_OK) // Get some user's data from database { Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; loginBySessionKeyOut->userCode = (int) Gbl.Usrs.Me.UsrDat.UsrCod; strncpy (loginBySessionKeyOut->userNickname,Gbl.Usrs.Me.UsrDat.Nickname,Nck_MAX_LENGTH_NICKNAME_WITHOUT_ARROBA); loginBySessionKeyOut->userNickname[Nck_MAX_LENGTH_NICKNAME_WITHOUT_ARROBA] = '\0'; strncpy (loginBySessionKeyOut->userID,Gbl.Usrs.Me.UsrDat.IDs.List[0].ID,255); // TODO: What user's ID? loginBySessionKeyOut->userID[255] = '\0'; strncpy (loginBySessionKeyOut->userSurname1,Gbl.Usrs.Me.UsrDat.Surname1,255); loginBySessionKeyOut->userSurname1[255] = '\0'; strncpy (loginBySessionKeyOut->userSurname2,Gbl.Usrs.Me.UsrDat.Surname2,255); loginBySessionKeyOut->userSurname2[255] = '\0'; strncpy (loginBySessionKeyOut->userFirstname,Gbl.Usrs.Me.UsrDat.FirstName,255); loginBySessionKeyOut->userFirstname[255] = '\0'; Pho_BuildLinkToPhoto (&Gbl.Usrs.Me.UsrDat,PhotoURL,false); strncpy (loginBySessionKeyOut->userPhoto,PhotoURL,PATH_MAX); loginBySessionKeyOut->userPhoto[PATH_MAX] = '\0'; strncpy (loginBySessionKeyOut->userBirthday,Gbl.Usrs.Me.UsrDat.Birthday.YYYYMMDD,4+2+2); loginBySessionKeyOut->userBirthday[4+2+2] = '\0'; loginBySessionKeyOut->userRole = Svc_GetRoleFromInternalRole (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB); } else { loginBySessionKeyOut->userCode = -1; loginBySessionKeyOut->userID = NULL; loginBySessionKeyOut->userSurname1 = NULL; loginBySessionKeyOut->userSurname2 = NULL; loginBySessionKeyOut->userFirstname = NULL; loginBySessionKeyOut->userPhoto = NULL; loginBySessionKeyOut->userRole = 0; } /***** Get degree (row[1]) *****/ Gbl.CurrentDeg.Deg.DegCod = Str_ConvertStrCodToLongCod (row[1]); Deg_GetDataOfDegreeByCod (&Gbl.CurrentDeg.Deg); loginBySessionKeyOut->degreeCode = (int) Gbl.CurrentDeg.Deg.DegCod; strncpy (loginBySessionKeyOut->degreeName,Gbl.CurrentDeg.Deg.FullName,255); loginBySessionKeyOut->degreeName[255] = '\0'; } /***** 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 (NumRows == 1) /***** 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 e-mail ***********************/ /*****************************************************************************/ int swad__getNewPassword (struct soap *soap, char *userID,char *appKey, // input struct swad__getNewPasswordOutput *getNewPasswordOut) // output { int ReturnCode; char UsrIDNickOrEmail[512]; char Query[512]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRows; char NewRandomPlainPassword[Pwd_MAX_LENGTH_PLAIN_PASSWORD+1]; 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 e-mail, @nickname or ID are valid *****/ strncpy (UsrIDNickOrEmail,userID,255); UsrIDNickOrEmail[255] = '\0'; if (Nck_CheckIfNickWithArrobaIsValid (UsrIDNickOrEmail)) // 1: It's a nickname { Str_RemoveLeadingArrobas (UsrIDNickOrEmail); /* User has typed a nickname */ sprintf (Query,"SELECT UsrCod" " FROM usr_nicknames" " WHERE Nickname='%s'", UsrIDNickOrEmail); } else if (Mai_CheckIfEmailIsValid (Gbl.Usrs.Me.UsrIdLogin)) // 2: It's an e-mail { /* User has typed an e-mail */ // TODO: Get only if email confirmed? sprintf (Query,"SELECT UsrCod" " FROM usr_emails" " WHERE E_mail='%s'", UsrIDNickOrEmail); } else // 3: It's not a nickname nor e-mail { // 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? sprintf (Query,"SELECT UsrCod" " FROM usr_IDs" " WHERE UsrID='%s'", UsrIDNickOrEmail); } else // String is not a valid user's nickname, e-mail or ID return soap_receiver_fault (Gbl.soap, "Bad log in", "User's e-mail, nickname or ID don't exist"); } /***** Get user's data from database *****/ if ((NumRows = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get user's data")) == 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); // 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; char Query[512]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow,NumRows; Rol_Role_t Role; 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 ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,-1L)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; /***** Query my courses from database *****/ sprintf (Query,"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); NumRows = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get user's courses"); 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,256); strncpy (getCoursesOut->coursesArray.__ptr[NumRow].courseShortName,row[1],255); getCoursesOut->coursesArray.__ptr[NumRow].courseShortName[255] = '\0'; /* Get course full name (row[2]) */ getCoursesOut->coursesArray.__ptr[NumRow].courseFullName = (char *) soap_malloc (Gbl.soap,256); strncpy (getCoursesOut->coursesArray.__ptr[NumRow].courseFullName,row[2],255); getCoursesOut->coursesArray.__ptr[NumRow].courseFullName[255] = '\0'; /* Get role (row[3]) */ if (sscanf (row[3],"%u",&Role) != 1) // Role in this course Role = Rol_UNKNOWN; getCoursesOut->coursesArray.__ptr[NumRow].userRole = Svc_GetRoleFromInternalRole (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; 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 }; Gbl.soap = soap; Gbl.WebService.Function = Svc_getCourseInfo; Gbl.CurrentCrs.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.CurrentCrs.Crs.CrsCod,-1L)) != SOAP_OK) return ReturnCode; /***** Get some of my data *****/ if ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.CurrentCrs.Crs.CrsCod)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; /***** Check if I am a student or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_STUDENT && Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_TEACHER) 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"); Inf_GetInfoSrcFromDB (Gbl.CurrentCrs.Crs.CrsCod,InfoType,&InfoSrc,&MustBeRead); getCourseInfo->infoSrc = (char *) soap_malloc (Gbl.soap,strlen (NamesInWSForInfoSrc[InfoSrc]) + 1); strcpy (getCourseInfo->infoSrc,NamesInWSForInfoSrc[InfoSrc]); /***** Set paths *****/ Deg_InitCurrentCourse (); /***** 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 (InfoType) { case Inf_LECTURES: // Syllabus (lectures) case Inf_PRACTICALS: // Syllabys (practicals) Result = Syl_WriteSyllabusIntoHTMLBuffer (InfoType,&(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 (InfoType,&(getCourseInfo->infoTxt)); break; case Inf_INFO_SRC_PAGE: // Web page hosted in SWAD server Result = Inf_WritePageIntoHTMLBuffer (InfoType,&(getCourseInfo->infoTxt)); break; case Inf_INFO_SRC_URL: // Link to a web page getCourseInfo->infoTxt = (char *) soap_malloc (Gbl.soap,Cns_MAX_BYTES_URL+1); Inf_WriteURLIntoTxtBuffer (InfoType,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; } /*****************************************************************************/ /********************** Return students in a course **************************/ /*****************************************************************************/ // If groupCode <= 0 ==> get users from the whole course int swad__getUsers (struct soap *soap, char *wsKey,int courseCode,int groupCode,int userRole, // input struct swad__getUsersOutput *getUsersOut) // output { int ReturnCode; char Query[512]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow,NumRows; bool UsrIDIsVisible; Gbl.soap = soap; Gbl.WebService.Function = Svc_getUsers; Gbl.CurrentCrs.Crs.CrsCod = (long) courseCode; Gbl.CurrentCrs.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"); /***** Check course and group codes *****/ if ((ReturnCode = Svc_CheckCourseAndGroupCodes (Gbl.CurrentCrs.Crs.CrsCod,Gbl.CurrentCrs.Grps.GrpCod)) != SOAP_OK) return ReturnCode; /***** Get some of my data *****/ if ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.CurrentCrs.Crs.CrsCod)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; /***** Check if I am a student or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_STUDENT && Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_TEACHER) 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; /***** Check requested users' type *****/ if (userRole != 2 && // student userRole != 3) // teacher { sprintf (Gbl.Message,"Only students (user's type = %u) or teachers (user's type = %u) are allowed", (unsigned) 2, // student (unsigned) 3); // teacher return soap_sender_fault (Gbl.soap, "Bad requested users' type", Gbl.Message); } /***** Query users beloging to course or group from database *****/ if (groupCode <= 0) // Users belonging to the whole course sprintf (Query,"SELECT usr_data.UsrCod" " FROM crs_usr,usr_data" " WHERE crs_usr.CrsCod='%ld' AND crs_usr.UsrCod=usr_data.UsrCod AND crs_usr.Role='%d'" " ORDER BY usr_data.Surname1,usr_data.Surname2,usr_data.FirstName,usr_data.UsrCod", (long) courseCode, userRole == 2 ? (unsigned) Rol_STUDENT : (unsigned) Rol_TEACHER); else // Users belonging to the group sprintf (Query,"SELECT usr_data.UsrCod" " FROM crs_grp_usr,crs_usr,usr_data" " WHERE crs_grp_usr.GrpCod='%ld' AND crs_grp_usr.UsrCod=crs_usr.UsrCod AND crs_grp_usr.UsrCod=usr_data.UsrCod AND crs_usr.Role='%d'" " ORDER BY usr_data.Surname1,usr_data.Surname2,usr_data.FirstName,usr_data.UsrCod", (long) groupCode, userRole == 2 ? (unsigned) Rol_STUDENT : (unsigned) Rol_TEACHER); NumRows = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get users"); getUsersOut->numUsers = (int) NumRows; getUsersOut->usersArray.__size = (int) NumRows; if (NumRows == 0) getUsersOut->usersArray.__ptr = NULL; else // Users found { getUsersOut->usersArray.__ptr = soap_malloc (Gbl.soap,(getUsersOut->usersArray.__size) * sizeof (*(getUsersOut->usersArray.__ptr))); /***** Users' IDs are visible? *****/ UsrIDIsVisible = (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB == Rol_STUDENT && userRole == 2); // get students in the course 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 user's data */ if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat)) /* Copy user's data into output structure */ Svc_CopyUsrData (&(getUsersOut->usersArray.__ptr[NumRow]),&Gbl.Usrs.Other.UsrDat,UsrIDIsVisible); } } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); return SOAP_OK; } /*****************************************************************************/ /********************** Return group types in a course ***********************/ /*****************************************************************************/ int swad__getGroupTypes (struct soap *soap, char *wsKey,int courseCode, // input struct swad__getGroupTypesOutput *getGroupTypesOut) // output { int ReturnCode; char Query[512]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow,NumRows; long OpenTime; Gbl.soap = soap; Gbl.WebService.Function = Svc_getGroupTypes; Gbl.CurrentCrs.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.CurrentCrs.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 ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.CurrentCrs.Crs.CrsCod)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; /***** Check if I am a student or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_STUDENT && Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_TEACHER) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to course"); /***** Query group types in a course from database *****/ sprintf (Query,"SELECT GrpTypCod,GrpTypName,Mandatory,Multiple,UNIX_TIMESTAMP(OpenTime)" " FROM crs_grp_types" " WHERE CrsCod='%d'" " ORDER BY GrpTypName", courseCode); NumRows = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get group types"); 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,256); strncpy (getGroupTypesOut->groupTypesArray.__ptr[NumRow].groupTypeName,row[1],255); getGroupTypesOut->groupTypesArray.__ptr[NumRow].groupTypeName[255] = '\0'; /* Get whether enrollment is mandatory ('Y') or voluntary ('N') (row[2]) */ getGroupTypesOut->groupTypesArray.__ptr[NumRow].mandatory = (row[2][0] == 'Y') ? 1 : 0; /* Get whether user can enroll 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; char Query[512]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow,NumRows; long GrpCod; unsigned MaxStudents; Gbl.soap = soap; Gbl.WebService.Function = Svc_getGroups; Gbl.CurrentCrs.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.CurrentCrs.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 ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.CurrentCrs.Crs.CrsCod)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; /***** Check if I am a student or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_STUDENT && Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_TEACHER) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to course"); /***** Query groups in a course from database *****/ sprintf (Query,"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); NumRows = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get user's groups"); 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,256); strncpy (getGroupsOut->groupsArray.__ptr[NumRow].groupTypeName,row[1],255); getGroupsOut->groupsArray.__ptr[NumRow].groupTypeName[255] = '\0'; /* 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,256); strncpy (getGroupsOut->groupsArray.__ptr[NumRow].groupName,row[3],255); getGroupsOut->groupsArray.__ptr[NumRow].groupName[255] = '\0'; /* 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_CountNumStdsInGrp (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; char Query[512]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow,NumRows; long GrpCod; unsigned MaxStudents; Gbl.soap = soap; Gbl.WebService.Function = Svc_sendMyGroups; Gbl.CurrentCrs.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.CurrentCrs.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 ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.CurrentCrs.Crs.CrsCod)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; /***** Check if I am a student or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_STUDENT && Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_TEACHER) 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.GrpCod = 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.GrpCod = (long *) calloc (LstGrpsIWant.NumGrps,sizeof (long))) == NULL) Lay_ShowErrorAndExit ("Not enough memory to store the codes of the selected groups."); 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.GrpCod[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 *****/ sprintf (Query,"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); NumRows = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get user's groups"); 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,256); strncpy (SendMyGroupsOut->groupsArray.__ptr[NumRow].groupTypeName,row[1],255); SendMyGroupsOut->groupsArray.__ptr[NumRow].groupTypeName[255] = '\0'; /* 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,256); strncpy (SendMyGroupsOut->groupsArray.__ptr[NumRow].groupName,row[3],255); SendMyGroupsOut->groupsArray.__ptr[NumRow].groupName[255] = '\0'; /* 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_CountNumStdsInGrp (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[PATH_MAX+1]; /* Copy user's code */ Usr->userCode = UsrDat->UsrCod; /* Copy user's nickname */ Usr->userNickname = (char *) soap_malloc (Gbl.soap,Nck_MAX_LENGTH_NICKNAME_WITHOUT_ARROBA+1); strncpy (Usr->userNickname,UsrDat->Nickname,Nck_MAX_LENGTH_NICKNAME_WITHOUT_ARROBA); Usr->userNickname[Nck_MAX_LENGTH_NICKNAME_WITHOUT_ARROBA] = '\0'; /* Copy user's ID */ Usr->userID = (char *) soap_malloc (Gbl.soap,ID_MAX_LENGTH_USR_ID+1); if (UsrIDIsVisible && UsrDat->IDs.List) { strncpy (Usr->userID,UsrDat->IDs.List[0].ID,ID_MAX_LENGTH_USR_ID); Usr->userID[ID_MAX_LENGTH_USR_ID] = '\0'; } else // Hide user's ID strcpy (Usr->userID,"********"); /* Copy user's surname1 */ Usr->userSurname1 = (char *) soap_malloc (Gbl.soap,256); strncpy (Usr->userSurname1,UsrDat->Surname1,255); Usr->userSurname1[255] = '\0'; /* Copy user's surname2 */ Usr->userSurname2 = (char *) soap_malloc (Gbl.soap,256); strncpy (Usr->userSurname2,UsrDat->Surname2,255); Usr->userSurname2[255] = '\0'; /* Copy user's first name */ Usr->userFirstname = (char *) soap_malloc (Gbl.soap,256); strncpy (Usr->userFirstname,UsrDat->FirstName,255); Usr->userFirstname[255] = '\0'; /* User's photo URL */ Pho_BuildLinkToPhoto (UsrDat,PhotoURL,false); Usr->userPhoto = (char *) soap_malloc (Gbl.soap,PATH_MAX+1); strncpy (Usr->userPhoto,PhotoURL,PATH_MAX); Usr->userPhoto[PATH_MAX] = '\0'; } /*****************************************************************************/ /***************** 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; char Query[512]; MYSQL_RES *mysql_res; MYSQL_ROW row; int NumAttEvent; long AttCod; char PhotoURL[PATH_MAX+1]; long StartTime; long EndTime; size_t Length; Gbl.soap = soap; Gbl.WebService.Function = Svc_getAttendanceEvents; Gbl.CurrentCrs.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.CurrentCrs.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 ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.CurrentCrs.Crs.CrsCod)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; /***** Check if I am a teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_TEACHER) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must be a teacher"); /***** Query list of attendance events *****/ sprintf (Query,"SELECT AttCod,UsrCod," "UNIX_TIMESTAMP(StartTime) AS ST,UNIX_TIMESTAMP(EndTime) AS ET," "CommentTchVisible,Title,Txt" " FROM att_events" " WHERE CrsCod='%d' AND Hidden='N'" " ORDER BY ST DESC,ET DESC,Title DESC", courseCode); getAttendanceEventsOut->eventsArray.__size = getAttendanceEventsOut->numEvents = (int) DB_QuerySELECT (Query,&mysql_res,"can not get attendance events"); 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 user's code of the user who created the event (row[1]) */ Gbl.Usrs.Other.UsrDat.UsrCod = Str_ConvertStrCodToLongCod (row[1]); if (Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,Gbl.CurrentCrs.Crs.CrsCod) == SOAP_OK) // 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); strcpy (getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].userSurname1, Gbl.Usrs.Other.UsrDat.Surname1); Length = strlen (Gbl.Usrs.Other.UsrDat.Surname2); getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].userSurname2 = (char *) soap_malloc (Gbl.soap,Length + 1); strcpy (getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].userSurname2, Gbl.Usrs.Other.UsrDat.Surname2); Length = strlen (Gbl.Usrs.Other.UsrDat.FirstName); getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].userFirstname = (char *) soap_malloc (Gbl.soap,Length + 1); strcpy (getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].userFirstname, Gbl.Usrs.Other.UsrDat.FirstName); Pho_BuildLinkToPhoto (&Gbl.Usrs.Other.UsrDat,PhotoURL,false); Length = strlen (PhotoURL); getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].userPhoto = (char *) soap_malloc (Gbl.soap,Length + 1); strcpy (getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].userPhoto, PhotoURL); } 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[2]) */ StartTime = 0L; if (row[2]) sscanf (row[2],"%ld",&StartTime); getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].startTime = StartTime; /* Get event end time (row[3]) */ EndTime = 0L; if (row[3]) sscanf (row[3],"%ld",&EndTime); getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].endTime = EndTime; /* Get whether teachers comments are visible ('Y') or hidden ('N') (row[4]) */ getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].commentsTeachersVisible = (row[4][0] == 'Y') ? 1 : 0; /* Get title of the event (row[5]) */ Length = strlen (row[5]); getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].title = (char *) soap_malloc (Gbl.soap,Length + 1); strcpy (getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].title, row[5]); /* Get Txt (row[6]) */ Length = strlen (row[6]); getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].text = (char *) soap_malloc (Gbl.soap,Length + 1); strcpy (getAttendanceEventsOut->eventsArray.__ptr[NumAttEvent].text, row[6]); /* Get list of groups for this attendance event */ Svc_GetListGrpsInAttendanceEvent (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_GetListGrpsInAttendanceEvent (long AttCod,char **ListGroups) { char Query[128]; MYSQL_RES *mysql_res; MYSQL_ROW row; long NumGrps; long NumGrp; long GrpCod; char GrpCodStr[10+1]; /***** Get list of groups *****/ sprintf (Query,"SELECT GrpCod FROM att_grp WHERE AttCod='%ld'", AttCod); if ((NumGrps = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get groups of an attendance event")) == 0) *ListGroups = NULL; else // Events found { *ListGroups = soap_malloc (Gbl.soap,NumGrps * 10); 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]); sprintf (GrpCodStr, NumGrp ? ",%ld" : "%ld", GrpCod); strcat (*ListGroups,GrpCodStr); } } /***** 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; const char *Ptr; char LongStr[1+10+1]; unsigned NumGrp; Gbl.soap = soap; Gbl.WebService.Function = Svc_sendAttendanceEvent; Gbl.CurrentCrs.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.CurrentCrs.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 ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.CurrentCrs.Crs.CrsCod)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; /***** Check if I am a teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_TEACHER) 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 */ strncpy (Att.Title,title,Att_MAX_LENGTH_ATTENDANCE_EVENT_TITLE); Att.Title[Att_MAX_LENGTH_ATTENDANCE_EVENT_TITLE] = '\0'; /***** Count number of groups *****/ for (Ptr = groups, NumGrp = 0; *Ptr; NumGrp++) Str_GetNextStringUntilComma (&Ptr,LongStr,1+10); Gbl.CurrentCrs.Grps.LstGrpsSel.NumGrps = NumGrp; /***** Create a list of groups selected *****/ if (Gbl.CurrentCrs.Grps.LstGrpsSel.NumGrps) { // Here NestedCalls is always 0 Gbl.CurrentCrs.Grps.LstGrpsSel.NestedCalls++; // Here NestedCalls is always 1 if ((Gbl.CurrentCrs.Grps.LstGrpsSel.GrpCod = (long *) calloc (Gbl.CurrentCrs.Grps.LstGrpsSel.NumGrps,sizeof (long))) == NULL) Lay_ShowErrorAndExit ("Not enough memory to store the codes of the selected groups."); for (Ptr = groups, NumGrp = 0; *Ptr; ) { Str_GetNextStringUntilComma (&Ptr,LongStr,1+10); Gbl.CurrentCrs.Grps.LstGrpsSel.GrpCod[NumGrp] = Str_ConvertStrCodToLongCod (LongStr); if (Grp_CheckIfGroupBelongsToCourse (Gbl.CurrentCrs.Grps.LstGrpsSel.GrpCod[NumGrp],Gbl.CurrentCrs.Crs.CrsCod)) NumGrp++; } Gbl.CurrentCrs.Grps.LstGrpsSel.NumGrps = NumGrp; // Update number of 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; } /*****************************************************************************/ /*********** 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]; char Query[1024]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow,NumRows; char PhotoURL[PATH_MAX+1]; size_t Length; 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.CurrentCrs.Crs.CrsCod = Att.CrsCod; /***** Get some of my data *****/ if ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.CurrentCrs.Crs.CrsCod)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; /***** Check if I am a teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_TEACHER) 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_STUDENT, 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_STUDENT, Att.AttCod); // Query: list of users in attendance list + rest of users (subquery) sprintf (Query,"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); NumRows = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get users in an attendance event"); 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) == SOAP_OK) // 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); strcpy (getAttendanceUsersOut->usersArray.__ptr[NumRow].userNickname,Gbl.Usrs.Other.UsrDat.Nickname); 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); strcpy (getAttendanceUsersOut->usersArray.__ptr[NumRow].userID,Gbl.Usrs.Other.UsrDat.IDs.List[0].ID); Length = strlen (Gbl.Usrs.Other.UsrDat.Surname1); getAttendanceUsersOut->usersArray.__ptr[NumRow].userSurname1 = (char *) soap_malloc (Gbl.soap,Length + 1); strcpy (getAttendanceUsersOut->usersArray.__ptr[NumRow].userSurname1,Gbl.Usrs.Other.UsrDat.Surname1); Length = strlen (Gbl.Usrs.Other.UsrDat.Surname2); getAttendanceUsersOut->usersArray.__ptr[NumRow].userSurname2 = (char *) soap_malloc (Gbl.soap,Length + 1); strcpy (getAttendanceUsersOut->usersArray.__ptr[NumRow].userSurname2,Gbl.Usrs.Other.UsrDat.Surname2); Length = strlen (Gbl.Usrs.Other.UsrDat.FirstName); getAttendanceUsersOut->usersArray.__ptr[NumRow].userFirstname = (char *) soap_malloc (Gbl.soap,Length + 1); strcpy (getAttendanceUsersOut->usersArray.__ptr[NumRow].userFirstname,Gbl.Usrs.Other.UsrDat.FirstName); Pho_BuildLinkToPhoto (&Gbl.Usrs.Other.UsrDat,PhotoURL,false); Length = strlen (PhotoURL); getAttendanceUsersOut->usersArray.__ptr[NumRow].userPhoto = (char *) soap_malloc (Gbl.soap,Length + 1); strcpy (getAttendanceUsersOut->usersArray.__ptr[NumRow].userPhoto,PhotoURL); } 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]; long UsrCod; unsigned NumCodsInList; char SubQuery[256]; char *Query = NULL; // Initialized to avoid warning 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.CurrentCrs.Crs.CrsCod = Att.CrsCod; /***** Get some of my data *****/ if ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.CurrentCrs.Crs.CrsCod)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; /***** Check if I am a teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_TEACHER) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must be a teacher"); 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); /* Start query used to mark not present users as absent */ if ((Query = (char *) malloc (256 + NumCodsInList * (1+1+10+1))) == NULL) return soap_receiver_fault (Gbl.soap, "Not enough memory", "Not enough memory to store list of users"); sprintf (Query,"UPDATE att_usr SET Present='N'" " WHERE AttCod='%ld'", Att.AttCod); } for (Ptr = users; *Ptr; ) { /* Find next string in text until comma (leading and trailing spaces are removed) */ Str_GetNextStringUntilComma (&Ptr,LongStr,1+10); if ((UsrCod = Str_ConvertStrCodToLongCod (LongStr)) > 0) if (Usr_ChkIfUsrCodExists (UsrCod)) // The user must belong to course, // but it's not necessary he/she belongs to groups associated to the event if (Usr_CheckIfUsrBelongsToCrs (UsrCod, Gbl.CurrentCrs.Crs.CrsCod, false)) { /* Mark user as present */ Att_RegUsrInAttEventNotChangingComments (Att.AttCod,UsrCod); /* Add this user to query used to mark not present users as absent */ if (setOthersAsAbsent) { sprintf (SubQuery,sendAttendanceUsersOut->numUsers ? ",'%ld'" : " AND UsrCod NOT IN ('%ld'", UsrCod); strcat (Query,SubQuery); } sendAttendanceUsersOut->numUsers++; } } if (setOthersAsAbsent) { /* Mark not present users as absent in table of users */ if (sendAttendanceUsersOut->numUsers) strcat (Query,")"); DB_QueryUPDATE (Query,"can not set other users as absent"); free ((void *) Query); /* Clean table att_usr */ Att_RemoveUsrsAbsentWithoutCommentsFromAttEvent (Att.AttCod); } 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; char Query[512]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRows; unsigned NumRow; long NtfCod; Ntf_NotifyEvent_t NotifyEvent; long EventTime; char PhotoURL[PATH_MAX+1]; struct Institution Ins; struct Centre Ctr; struct Degree Deg; struct Course Crs; long Cod; char ForumName[512]; char *SummaryStr; char *ContentStr; Ntf_Status_t Status; 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 ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,-1L)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; /***** Get my language from database *****/ if ((ReturnCode = Svc_GetMyLanguage ()) != SOAP_OK) return ReturnCode; if ((SummaryStr = malloc (Cns_MAX_BYTES_TEXT+1)) == NULL) return soap_receiver_fault (Gbl.soap, "Not enough memory", "Not enough memory to store the summary of the notification"); /***** Get my notifications from database *****/ sprintf (Query,"SELECT NtfCod,NotifyEvent,UNIX_TIMESTAMP(TimeNotif)," "FromUsrCod,InsCod,CtrCod,DegCod,CrsCod,Cod,Status" " FROM notif" " WHERE ToUsrCod='%ld' AND TimeNotif>=FROM_UNIXTIME('%ld')" " ORDER BY TimeNotif DESC", Gbl.Usrs.Me.UsrDat.UsrCod,beginTime); NumRows = DB_QuerySELECT (Query,&mysql_res,"can not get user's notifications"); getNotificationsOut->numNotifications = (int) NumRows; getNotificationsOut->notificationsArray.__size = (int) NumRows; if (NumRows == 0) getNotificationsOut->notificationsArray.__ptr = NULL; else // Notifications found { getNotificationsOut->notificationsArray.__ptr = soap_malloc (Gbl.soap,(getNotificationsOut->notificationsArray.__size) * sizeof (*(getNotificationsOut->notificationsArray.__ptr))); for (NumRow = 0; NumRow < NumRows; NumRow++) { /* Get next notification */ row = mysql_fetch_row (mysql_res); /* Get unique notification code (row[0]) */ NtfCod = Str_ConvertStrCodToLongCod (row[0]); getNotificationsOut->notificationsArray.__ptr[NumRow].notifCode = (int) NtfCod; /* Get notification event type (row[1]) */ NotifyEvent = Ntf_GetNotifyEventFromDB ((const char *) row[1]); getNotificationsOut->notificationsArray.__ptr[NumRow].eventType = (char *) soap_malloc (Gbl.soap,256); strncpy (getNotificationsOut->notificationsArray.__ptr[NumRow].eventType,Ntf_WSNotifyEvents[NotifyEvent],255); getNotificationsOut->notificationsArray.__ptr[NumRow].eventType[255] = '\0'; /* Get time of the event (row[2]) */ EventTime = 0L; if (row[2]) sscanf (row[2],"%ld",&EventTime); getNotificationsOut->notificationsArray.__ptr[NumRow].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) == SOAP_OK) // Get some user's data from database { getNotificationsOut->notificationsArray.__ptr[NumRow].userNickname = (char *) soap_malloc (Gbl.soap,Nck_MAX_LENGTH_NICKNAME_WITHOUT_ARROBA+1); strncpy (getNotificationsOut->notificationsArray.__ptr[NumRow].userNickname,Gbl.Usrs.Other.UsrDat.Nickname,Nck_MAX_LENGTH_NICKNAME_WITHOUT_ARROBA); getNotificationsOut->notificationsArray.__ptr[NumRow].userNickname[Nck_MAX_LENGTH_NICKNAME_WITHOUT_ARROBA] = '\0'; getNotificationsOut->notificationsArray.__ptr[NumRow].userSurname1 = (char *) soap_malloc (Gbl.soap,256); strncpy (getNotificationsOut->notificationsArray.__ptr[NumRow].userSurname1,Gbl.Usrs.Other.UsrDat.Surname1,255); getNotificationsOut->notificationsArray.__ptr[NumRow].userSurname1[255] = '\0'; getNotificationsOut->notificationsArray.__ptr[NumRow].userSurname2 = (char *) soap_malloc (Gbl.soap,256); strncpy (getNotificationsOut->notificationsArray.__ptr[NumRow].userSurname2,Gbl.Usrs.Other.UsrDat.Surname2,255); getNotificationsOut->notificationsArray.__ptr[NumRow].userSurname2[255] = '\0'; getNotificationsOut->notificationsArray.__ptr[NumRow].userFirstname = (char *) soap_malloc (Gbl.soap,256); strncpy (getNotificationsOut->notificationsArray.__ptr[NumRow].userFirstname,Gbl.Usrs.Other.UsrDat.FirstName,255); getNotificationsOut->notificationsArray.__ptr[NumRow].userFirstname[255] = '\0'; Pho_BuildLinkToPhoto (&Gbl.Usrs.Other.UsrDat,PhotoURL,false); getNotificationsOut->notificationsArray.__ptr[NumRow].userPhoto = (char *) soap_malloc (Gbl.soap,PATH_MAX+1); strncpy (getNotificationsOut->notificationsArray.__ptr[NumRow].userPhoto,PhotoURL,PATH_MAX); getNotificationsOut->notificationsArray.__ptr[NumRow].userPhoto[PATH_MAX] = '\0'; } else { getNotificationsOut->notificationsArray.__ptr[NumRow].userNickname = NULL; getNotificationsOut->notificationsArray.__ptr[NumRow].userSurname1 = NULL; getNotificationsOut->notificationsArray.__ptr[NumRow].userSurname2 = NULL; getNotificationsOut->notificationsArray.__ptr[NumRow].userFirstname = NULL; getNotificationsOut->notificationsArray.__ptr[NumRow].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[NumRow].eventCode = (int) Cod; /* Set location */ getNotificationsOut->notificationsArray.__ptr[NumRow].location = (char *) soap_malloc (Gbl.soap,1024); if (NotifyEvent == Ntf_EVENT_FORUM_POST_COURSE || NotifyEvent == Ntf_EVENT_FORUM_REPLY) { For_SetForumName (For_GetForumTypeOfAPost (Cod), &Ins, &Ctr, &Deg, &Crs, ForumName,Gbl.Prefs.Language,false); // Set forum name in recipient's language sprintf (getNotificationsOut->notificationsArray.__ptr[NumRow].location,"%s: %s", Txt_Forum,ForumName); } else if (Crs.CrsCod > 0) sprintf (getNotificationsOut->notificationsArray.__ptr[NumRow].location,"%s: %s", Txt_Course,Crs.ShortName); else if (Deg.DegCod > 0) sprintf (getNotificationsOut->notificationsArray.__ptr[NumRow].location,"%s: %s", Txt_Degree,Deg.ShortName); else if (Ctr.CtrCod > 0) sprintf (getNotificationsOut->notificationsArray.__ptr[NumRow].location,"%s: %s", Txt_Centre,Ctr.ShortName); else if (Ins.InsCod > 0) sprintf (getNotificationsOut->notificationsArray.__ptr[NumRow].location,"%s: %s", Txt_Institution,Ins.ShortName); else strcpy (getNotificationsOut->notificationsArray.__ptr[NumRow].location,"-"); /* Get status (row[9]) */ if (sscanf (row[9],"%u",&Status) != 1) Status = (Ntf_Status_t) 0; getNotificationsOut->notificationsArray.__ptr[NumRow].status = (int) Status; /* Get summary and content */ ContentStr = NULL; Ntf_GetNotifSummaryAndContent (SummaryStr,&ContentStr,NotifyEvent, Cod,Crs.CrsCod,Gbl.Usrs.Me.UsrDat.UsrCod, Cfg_MAX_CHARS_NOTIF_SUMMARY_WEB_SERVICE,true); getNotificationsOut->notificationsArray.__ptr[NumRow].summary = (char *) soap_malloc (Gbl.soap,strlen (SummaryStr)+1); strcpy (getNotificationsOut->notificationsArray.__ptr[NumRow].summary,SummaryStr); if (ContentStr != NULL) { getNotificationsOut->notificationsArray.__ptr[NumRow].content = (char *) soap_malloc (Gbl.soap,strlen (ContentStr)+1); strcpy (getNotificationsOut->notificationsArray.__ptr[NumRow].content,ContentStr); free ((void *) ContentStr); ContentStr = NULL; } else { getNotificationsOut->notificationsArray.__ptr[NumRow].content = (char *) soap_malloc (Gbl.soap,1); getNotificationsOut->notificationsArray.__ptr[NumRow].content[0] = '\0'; } } } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); /***** Free summary *****/ free ((void *) SummaryStr); return SOAP_OK; } /*****************************************************************************/ /********************* Get my language from database *************************/ /*****************************************************************************/ static int Svc_GetMyLanguage (void) { extern const char *Txt_STR_LANG_ID[1+Txt_NUM_LANGUAGES]; char Query[512]; MYSQL_RES *mysql_res; MYSQL_ROW row; Txt_Language_t Lan; /***** Get user's language *****/ sprintf (Query,"SELECT Language FROM usr_data" " WHERE UsrCod='%ld'", Gbl.Usrs.Me.UsrDat.UsrCod); if (DB_QuerySELECT (Query,&mysql_res,"can not get user's language") != 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 = Txt_LANGUAGE_UNKNOWN; // Language unknown for (Lan = (Txt_Language_t) 1; Lan <= Txt_NUM_LANGUAGES; Lan++) if (!strcasecmp (row[0],Txt_STR_LANG_ID[Lan])) { Gbl.Prefs.Language = Lan; break; } if (Gbl.Prefs.Language == Txt_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; char Query[512]; 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 ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,-1L)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; 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 *****/ sprintf (Query,"UPDATE notif SET Status=(Status | %u)" " WHERE NtfCod='%ld' AND ToUsrCod='%ld'", (unsigned) Ntf_STATUS_BIT_READ, (long) NtfCod,Gbl.Usrs.Me.UsrDat.UsrCod); DB_QueryUPDATE (Query,"can not mark notification as read"); NumNtfsMarkedAsRead++; } } } /***** Return notification code *****/ markNotificationsAsReadOut->numNotifications = (int) NumNtfsMarkedAsRead; return SOAP_OK; } /*****************************************************************************/ /****************** Send a message to one or more users **********************/ /*****************************************************************************/ #define Svc_MAX_LENGHT_QUERY_RECIPIENTS (10*1024) 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_WITH_ARROBA+1]; char Query[Svc_MAX_LENGHT_QUERY_RECIPIENTS+1]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow,NumRows; bool FirstNickname = true; bool ThereAreNicknames = false; const char *Ptr; bool NotifyByEmail; 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 ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,-1L)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; /***** Check if the message is a reply to a previous message *****/ if (messageCode) { /***** Check if the original message was really received by me *****/ sprintf (Query,"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); if (!DB_QuerySELECT (Query,&mysql_res,"can not check original message")) 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 *****/ sprintf (Query,"SELECT UsrCod FROM msg_snt" " WHERE MsgCod='%ld'" " UNION " "SELECT UsrCod FROM msg_snt_deleted" " WHERE MsgCod='%ld'", (long) messageCode,(long) messageCode); if ((NumRows = DB_QuerySELECT (Query,&mysql_res,"can not check original message"))) // 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"); } /***** Build query for recipients from database *****/ if (ReplyUsrCod > 0) sprintf (Query,"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_WITH_ARROBA); /* 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_LENGTH_NICKNAME_WITHOUT_ARROBA+32 > Svc_MAX_LENGHT_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) strcat (Query," UNION "); strcat (Query,"SELECT UsrCod FROM usr_nicknames" " WHERE Nickname IN ('"); FirstNickname = false; ThereAreNicknames = true; } else strcat (Query,",'"); strcat (Query,Nickname); strcat (Query,"'"); } } if (ThereAreNicknames) strcat (Query,")"); /***** 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 (Query,&mysql_res,"can not get users"); 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)) { /* This received message must be notified by e-mail? */ NotifyByEmail = ((Gbl.Usrs.Other.UsrDat.UsrCod != Gbl.Usrs.Me.UsrDat.UsrCod) && (Gbl.Usrs.Other.UsrDat.Prefs.EmailNtfEvents & (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 ************************/ /*****************************************************************************/ 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; char Query[512+Cns_MAX_BYTES_SUBJECT+Cns_MAX_BYTES_LONG_TEXT]; /***** Create message *****/ if (!MsgAlreadyInserted) // The message is inserted only once in the table of messages sent { /***** Insert message subject and body in the database *****/ /* Build query */ sprintf (Query,"INSERT INTO msg_content (Subject,Content)" " VALUES ('%s','%s')",Subject,Content); /* Get the code of the inserted item */ NewMsgCod = DB_QueryINSERTandReturnCode (Query,"can not create message"); /***** Insert message in sent messages *****/ sprintf (Query,"INSERT INTO msg_snt (MsgCod,CrsCod,UsrCod,Expanded,CreatTime)" " VALUES ('%ld','-1','%ld','N',NOW())", NewMsgCod,SenderUsrCod); DB_QueryINSERT (Query,"can not create message"); MsgAlreadyInserted = true; } /***** Insert message received in the database *****/ sprintf (Query,"INSERT INTO msg_rcv (MsgCod,UsrCod,Notified,Open,Replied,Expanded)" " VALUES ('%ld','%ld','%c','N','N','N')", NewMsgCod,RecipientUsrCod, NotifyByEmail ? 'Y' : 'N'); DB_QueryINSERT (Query,"can not create received message"); /***** Create notification for this recipient. If this recipient wants to receive notifications by e-mail, activate the sending of a notification *****/ sprintf (Query,"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)); DB_QueryINSERT (Query,"can not create new notification event"); /***** If this recipient is the original sender of a message been replied... *****/ if (RecipientUsrCod == ReplyUsrCod) { /***** ...then update received message setting Replied field to true *****/ sprintf (Query,"UPDATE msg_rcv SET Replied='Y' WHERE MsgCod='%ld' AND UsrCod='%ld'", OriginalMsgCod,SenderUsrCod); DB_QueryUPDATE (Query,"can not update a received message"); } 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; char Query[512+Cns_MAX_BYTES_TEXT]; long NotCod; Gbl.soap = soap; Gbl.WebService.Function = Svc_sendNotice; Gbl.CurrentCrs.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 ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.CurrentCrs.Crs.CrsCod)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; /***** Check course and group codes *****/ if ((ReturnCode = Svc_CheckCourseAndGroupCodes (Gbl.CurrentCrs.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.RoleInCurrentCrsDB != Rol_TEACHER) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must be a teacher"); /***** Insert notice in the database *****/ /* Build query */ sprintf (Query,"INSERT INTO notices (CrsCod,UsrCod,CreatTime,Content,Status)" " VALUES ('%ld','%ld',NOW(),'%s','%u')", Gbl.CurrentCrs.Crs.CrsCod,Gbl.Usrs.Me.UsrDat.UsrCod, body,(unsigned) Not_ACTIVE_NOTICE); /* Get the code of the inserted item */ NotCod = DB_QueryINSERTandReturnCode (Query,"can not create message"); /***** 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; Gbl.soap = soap; Gbl.WebService.Function = Svc_getTestConfig; Gbl.CurrentCrs.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 ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.CurrentCrs.Crs.CrsCod)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; /***** Check if course code is correct *****/ if (Gbl.CurrentCrs.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 or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_STUDENT && Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_TEACHER) 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,256); 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; // if (Gbl.Test.Config.FeedbackType == Tst_FEEDBACK_FULL_FEEDBACK) // Gbl.Test.Config.FeedbackType = Tst_FEEDBACK_EACH_GOOD_BAD; // TODO: remove this when SWADroid uses it strncpy (getTestConfigOut->feedback,Tst_FeedbackXML[Gbl.Test.Config.FeedbackType],255); getTestConfigOut->feedback[255] = '\0'; /***** 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) { char Query[512]; MYSQL_RES *mysql_res; MYSQL_ROW row; /***** Query database *****/ sprintf (Query,"SELECT Pluggable,Min,Def,Max,MinTimeNxtTstPerQst,Feedback" " FROM tst_config WHERE CrsCod='%ld'", CrsCod); if (DB_QuerySELECT (Query,&mysql_res,"can not get test configuration")) { /***** 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.FeedbackType = 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) { char Query[512]; /***** Get number of questions *****/ // Reject questions with any tag hidden // Select only questions with tags sprintf (Query,"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 (int) DB_QueryCOUNT (Query,"can not get number of test questions"); } /*****************************************************************************/ /************************* 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; Gbl.soap = soap; Gbl.WebService.Function = Svc_getTests; Gbl.CurrentCrs.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 ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.CurrentCrs.Crs.CrsCod)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; /***** Check if course code is correct *****/ if (Gbl.CurrentCrs.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 or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_STUDENT && Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_TEACHER) 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 *Txt_STR_LANG_ID[1+Txt_NUM_LANGUAGES]; char Query[512]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow,NumRows; /***** Get available tags from database *****/ sprintf (Query,"SELECT TagCod,TagTxt" " FROM tst_tags" " WHERE CrsCod='%ld' AND TagHidden='N'" " ORDER BY TagTxt", CrsCod); NumRows = DB_QuerySELECT (Query,&mysql_res,"can not get test tags"); 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,256); strncpy (getTestsOut->tagsArray.__ptr[NumRow].tagText,row[1],255); getTestsOut->tagsArray.__ptr[NumRow].tagText[255] = '\0'; } } /***** 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]; char Query[1024]; 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 sprintf (Query,"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); NumRows = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get test questions"); 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,256); strncpy (getTestsOut->questionsArray.__ptr[NumRow].answerType,Tst_StrAnswerTypesXML[AnswerType],255); getTestsOut->questionsArray.__ptr[NumRow].answerType[255] = '\0'; /* 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); strncpy (getTestsOut->questionsArray.__ptr[NumRow].stem,row[3],Cns_MAX_BYTES_TEXT); getTestsOut->questionsArray.__ptr[NumRow].stem[Cns_MAX_BYTES_TEXT] = '\0'; /* Get question feedback (row[4]) */ getTestsOut->questionsArray.__ptr[NumRow].feedback = (char *) soap_malloc (Gbl.soap,Cns_MAX_BYTES_TEXT+1); strncpy (getTestsOut->questionsArray.__ptr[NumRow].feedback,row[4],Cns_MAX_BYTES_TEXT); getTestsOut->questionsArray.__ptr[NumRow].feedback[Cns_MAX_BYTES_TEXT] = '\0'; } } /***** 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]; char Query[1024]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow,NumRows; unsigned Index; /***** Get recent test questions from database *****/ sprintf (Query,"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); NumRows = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get test answers"); 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); strncpy (getTestsOut->answersArray.__ptr[NumRow].answerText,row[3],Cns_MAX_BYTES_TEXT); getTestsOut->answersArray.__ptr[NumRow].answerText[Cns_MAX_BYTES_TEXT] = '\0'; /* Get feedback (row[4]) */ getTestsOut->answersArray.__ptr[NumRow].answerFeedback = (char *) soap_malloc (Gbl.soap,Cns_MAX_BYTES_TEXT+1); strncpy (getTestsOut->answersArray.__ptr[NumRow].answerFeedback,row[4],Cns_MAX_BYTES_TEXT); getTestsOut->answersArray.__ptr[NumRow].answerFeedback[Cns_MAX_BYTES_TEXT] = '\0'; } } /***** 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]; char Query[1024]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow,NumRows; unsigned Index; /***** Get recent test questions from database *****/ sprintf (Query,"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); NumRows = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get test question tags"); 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 *****************/ /*****************************************************************************/ 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[1024]; char DegStr[ 1+1+1+ 10 +1+ 1]; // DegStr=", ' - number ' \0" long DegCod; bool FirstDegree = true; char Query[4096]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned NumRow; unsigned NumRows; long QstCod = -1L; Tst_AnswerType_t AnswerType; unsigned Index; 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 ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.CurrentCrs.Crs.CrsCod)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; /***** Loop over recipients' nicknames building query *****/ DegreesStr[0] = '\0'; Ptr = degrees; /* if (Gbl.Usrs.Me.UsrDat.UsrCod == 19543) { char QueryDebug[512*1024]; sprintf (QueryDebug,"INSERT INTO debug (DebugTime,Txt) VALUES (NOW(),'degrees = %s')",degrees); DB_QueryINSERT (QueryDebug,"Error inserting in debug table"); } */ 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) { sprintf (DegreesStr,"'%ld'",DegCod); FirstDegree = false; } else { sprintf (DegStr,",'%ld'",DegCod); strcat (DegreesStr,DegStr); } } } 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 *****/ setlocale (LC_NUMERIC,"en_US.utf8"); // To print the floating point as a dot sprintf (Query,"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); setlocale (LC_NUMERIC,"es_ES.utf8"); // Return to spanish system (TODO: this should be internationalized!!!!!!!) NumRows = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get test questions"); if (NumRows == 1) // Question found { /* if (Gbl.Usrs.Me.UsrDat.UsrCod == 19543) { char QueryDebug[512*1024]; sprintf (QueryDebug,"INSERT INTO debug (DebugTime,Txt) VALUES (NOW(),'Una pregunta devuelta')"); DB_QueryINSERT (QueryDebug,"Error inserting in debug table"); } */ /* 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,256); strncpy (getTrivialQuestionOut->question.answerType,Tst_StrAnswerTypesXML[AnswerType],255); getTrivialQuestionOut->question.answerType[255] = '\0'; /* 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); strncpy (getTrivialQuestionOut->question.stem,row[3],Cns_MAX_BYTES_TEXT); getTrivialQuestionOut->question.stem[Cns_MAX_BYTES_TEXT] = '\0'; /* Get question feedback (row[4]) */ getTrivialQuestionOut->question.feedback = (char *) soap_malloc (Gbl.soap,Cns_MAX_BYTES_TEXT+1); strncpy (getTrivialQuestionOut->question.feedback,row[4],Cns_MAX_BYTES_TEXT); getTrivialQuestionOut->question.feedback[Cns_MAX_BYTES_TEXT] = '\0'; } else // Empty question { /* if (Gbl.Usrs.Me.UsrDat.UsrCod == 19543) { char QueryDebug[512*1024]; sprintf (QueryDebug,"INSERT INTO debug (DebugTime,Txt) VALUES (NOW(),'Ninguna pregunta devuelta')"); DB_QueryINSERT (QueryDebug,"Error inserting in debug table"); } */ /* 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 *****/ sprintf (Query,"SELECT QstCod,AnsInd,Correct,Answer,Feedback" " FROM tst_answers WHERE QstCod='%ld'" " ORDER BY AnsInd", QstCod); NumRows = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get test answers"); 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); strncpy (getTrivialQuestionOut->answersArray.__ptr[NumRow].answerText,row[3],Cns_MAX_BYTES_TEXT); getTrivialQuestionOut->answersArray.__ptr[NumRow].answerText[Cns_MAX_BYTES_TEXT] = '\0'; /* Get feedback (row[4]) */ getTrivialQuestionOut->answersArray.__ptr[NumRow].answerFeedback = (char *) soap_malloc (Gbl.soap,Cns_MAX_BYTES_TEXT+1); strncpy (getTrivialQuestionOut->answersArray.__ptr[NumRow].answerFeedback,row[4],Cns_MAX_BYTES_TEXT); getTrivialQuestionOut->answersArray.__ptr[NumRow].answerFeedback[Cns_MAX_BYTES_TEXT] = '\0'; } } /***** 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 PathXMLPriv[PATH_MAX+1]; char XMLFileName[PATH_MAX+1]; unsigned long FileSize; unsigned long NumBytesRead; Gbl.soap = soap; Gbl.WebService.Function = Svc_getDirectoryTree; Gbl.CurrentCrs.Crs.CrsCod = (long) courseCode; Gbl.CurrentCrs.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 ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.CurrentCrs.Crs.CrsCod)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; /***** Check course and group codes *****/ if ((ReturnCode = Svc_CheckCourseAndGroupCodes (Gbl.CurrentCrs.Crs.CrsCod,Gbl.CurrentCrs.Grps.GrpCod)) != SOAP_OK) return ReturnCode; /***** Check if I am a student or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_STUDENT && Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_TEACHER) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to course"); /***** Check if I belong to course/group *****/ if (Gbl.CurrentCrs.Grps.GrpCod > 0) if (!Grp_GetIfIBelongToGrp (Gbl.CurrentCrs.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_DOCUM_GRP; break; case 2: // Shared files Gbl.FileBrowser.Type = Brw_ADMI_SHARE_GRP; break; case 3: // Marks Gbl.FileBrowser.Type = Brw_SHOW_MARKS_GRP; break; } else // groupCode <= 0 switch (treeCode) { case 1: // Documents Gbl.FileBrowser.Type = Brw_SHOW_DOCUM_CRS; break; case 2: // Shared files Gbl.FileBrowser.Type = Brw_ADMI_SHARE_CRS; break; case 3: // Marks Gbl.FileBrowser.Type = Brw_SHOW_MARKS_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.CurrentCrs.Crs.CrsCod = (courseCode > 0) ? (long) courseCode : -1L; Gbl.CurrentCrs.Grps.GrpCod = (groupCode > 0) ? (long) groupCode : -1L; sprintf (Gbl.CurrentCrs.PathPriv,"%s/%s/%ld", Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_CRS,Gbl.CurrentCrs.Crs.CrsCod); Brw_InitializeFileBrowser (); Brw_SetFullPathInTree (Brw_RootFolderInternalNames[Gbl.FileBrowser.Type],"."); /* Check if exists the directory for HTML output. If not exists, create it */ sprintf (PathXMLPriv,"%s/%s",Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_OUT); Fil_CreateDirIfNotExists (PathXMLPriv); /* Create a unique name for the file */ sprintf (XMLFileName,"%s/%s.xml", PathXMLPriv,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]; char PathFileInExplTree[PATH_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 ".." { sprintf (PathFileRel ,"%s/%s",Path ,FileList[NumFile]->d_name); sprintf (PathFileInExplTree,"%s/%s",PathInTree,FileList[NumFile]->d_name); lstat (PathFileRel,&FileStatus); /***** 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[PATH_MAX+1]; /***** Is this row hidden or visible? *****/ if (Gbl.FileBrowser.Type == Brw_SHOW_DOCUM_CRS || Gbl.FileBrowser.Type == Brw_SHOW_DOCUM_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); Pho_BuildLinkToPhoto (&Gbl.Usrs.Me.UsrDat,PhotoURL,false); 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[PATH_MAX+1]; char PhotoURL[PATH_MAX+1]; 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,PATH_MAX+1); getFileOut->license = (char *) soap_malloc (Gbl.soap,256); getFileOut->publisherName = (char *) soap_malloc (Gbl.soap,255+1+255+1+255+1); getFileOut->publisherPhoto = (char *) soap_malloc (Gbl.soap,PATH_MAX+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.CurrentIns.Ins.InsCod, &Gbl.CurrentCtr.Ctr.CtrCod, &Gbl.CurrentDeg.Deg.DegCod, &Gbl.CurrentCrs.Crs.CrsCod, &Gbl.CurrentCrs.Grps.GrpCod); /***** Get some of my data *****/ if ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.CurrentCrs.Crs.CrsCod)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; /***** Check course and group codes *****/ if ((ReturnCode = Svc_CheckCourseAndGroupCodes (Gbl.CurrentCrs.Crs.CrsCod,Gbl.CurrentCrs.Grps.GrpCod)) != SOAP_OK) return ReturnCode; /***** Check if I am a student or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_STUDENT && Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_TEACHER) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to course"); /***** Check if I belong to group *****/ if (Gbl.CurrentCrs.Grps.GrpCod > 0) if (!Grp_GetIfIBelongToGrp (Gbl.CurrentCrs.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_SHOW_DOCUM_CRS: // This type of file browser is not in database // case Brw_SHOW_DOCUM_GRP: // This type of file browser is not in database case Brw_ADMI_DOCUM_CRS: case Brw_ADMI_DOCUM_GRP: case Brw_ADMI_SHARE_CRS: case Brw_ADMI_SHARE_GRP: break; case Brw_ADMI_MARKS_CRS: case Brw_ADMI_MARKS_GRP: // Downloading a file of marks is only allowed for teachers if (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_TEACHER) 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 *****/ Deg_InitCurrentCourse (); 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 *****/ strncpy (getFileOut->fileName,FileMetadata.FilFolLnkName,NAME_MAX); getFileOut->fileName[NAME_MAX] = '\0'; strncpy (getFileOut->URL,URL,PATH_MAX); getFileOut->URL[PATH_MAX] = '\0'; getFileOut->size = (int) FileMetadata.Size; getFileOut->time = (int) FileMetadata.Time; strncpy (getFileOut->license,Txt_LICENSES[FileMetadata.License],255); getFileOut->license[255] = '\0'; if ((Gbl.Usrs.Other.UsrDat.UsrCod = FileMetadata.PublisherUsrCod) > 0) /* Get publisher's data */ if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat)) /* Copy publisher's data into output structure */ { strncpy (getFileOut->publisherName,Gbl.Usrs.Other.UsrDat.FullName,255+1+255+1+255); getFileOut->publisherName[255+1+255+1+255] = '\0'; Pho_BuildLinkToPhoto (&Gbl.Usrs.Other.UsrDat,PhotoURL,false); strncpy (getFileOut->publisherPhoto,PhotoURL,PATH_MAX); getFileOut->publisherPhoto[PATH_MAX] = '\0'; } 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[NAME_MAX+1]; // Really not used char *ContentStr; 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_MARKS_CRS && FileMetadata.FileBrowser != Brw_ADMI_MARKS_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.CurrentIns.Ins.InsCod, &Gbl.CurrentCtr.Ctr.CtrCod, &Gbl.CurrentDeg.Deg.DegCod, &Gbl.CurrentCrs.Crs.CrsCod, &Gbl.CurrentCrs.Grps.GrpCod); /***** Check course and group codes *****/ if ((ReturnCode = Svc_CheckCourseAndGroupCodes (Gbl.CurrentCrs.Crs.CrsCod,Gbl.CurrentCrs.Grps.GrpCod)) != SOAP_OK) return ReturnCode; /***** Get some of my data *****/ if ((ReturnCode = Svc_GetSomeUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Gbl.CurrentCrs.Crs.CrsCod)) != SOAP_OK) return ReturnCode; Gbl.Usrs.Me.Logged = true; Gbl.Usrs.Me.LoggedRole = Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB; /***** Check if I am a student or teacher in the course *****/ if (Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_STUDENT && Gbl.Usrs.Me.UsrDat.RoleInCurrentCrsDB != Rol_TEACHER) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to course"); /***** Check if I belong to group *****/ if (Gbl.CurrentCrs.Grps.GrpCod > 0) if (!Grp_GetIfIBelongToGrp (Gbl.CurrentCrs.Grps.GrpCod)) return soap_receiver_fault (Gbl.soap, "Request forbidden", "Requester must belong to group"); /***** Get content *****/ ContentStr = NULL; Mrk_GetNotifMyMarks (SummaryStr,&ContentStr, FileMetadata.FilCod,Gbl.Usrs.Me.UsrDat.UsrCod, 0,true); if (ContentStr != NULL) { getMarksOut->content = (char *) soap_malloc (Gbl.soap,strlen (ContentStr)+1); strcpy (getMarksOut->content,ContentStr); free ((void *) ContentStr); ContentStr = NULL; } else { getMarksOut->content = (char *) soap_malloc (Gbl.soap,1); getMarksOut->content[0] = '\0'; } return SOAP_OK; }