/* Synchronet user logout routines */ /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * * * * Copyright Rob Swindell - http://www.synchro.net/copyright.html * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * See the GNU General Public License for more details: gpl.txt or * * http://www.fsf.org/copyleft/gpl.html * * * * For Synchronet coding style and modification guidelines, see * * http://www.synchro.net/source.html * * * * Note: If this box doesn't appear square, then you need to fix your tabs. * ****************************************************************************/ #include "sbbs.h" #include "cmdshell.h" /****************************************************************************/ /* Function that is called after a user hangs up or logs off */ /****************************************************************************/ void sbbs_t::logout() { char path[MAX_PATH+1]; char str[256]; char tmp[512]; int i,j; ushort ttoday; node_t node; struct tm tm; now=time(NULL); if(localtime_r(&now,&tm)==NULL) errormsg(WHERE,ERR_CHK,"localtime",(ulong)now); if(!useron.number) { /* Not logged in, so do nothing */ if(!online) { SAFEPRINTF2(str,"%s T:%3u sec\r\n" ,hhmmtostr(&cfg,&tm,tmp) ,(uint)(now-answertime)); logline("@-",str); } return; } lprintf(LOG_INFO, "logout initiated"); SAFECOPY(lastuseron,useron.alias); // TODO: race condition here if(!online && getnodedat(cfg.node_num, &node, /* lock: */true) == 0) { node.status = NODE_LOGOUT; putnodedat(cfg.node_num, &node); } if(useron.rest&FLAG('G')) { putuserstr(useron.number, USER_NAME, nulstr); clearbatdl(); } if(sys_status&SS_USERON && thisnode.status!=NODE_QUIET && !(useron.rest&FLAG('Q'))) for(i=1;i<=cfg.sys_nodes;i++) if(i!=cfg.node_num) { getnodedat(i,&node,0); if((node.status==NODE_INUSE || node.status==NODE_QUIET) && !(node.misc&NODE_AOFF) && node.useron!=useron.number) { SAFEPRINTF2(str,text[NodeLoggedOff],cfg.node_num ,thisnode.misc&NODE_ANON ? text[UNKNOWN_USER] : useron.alias); putnmsg(i, str); } } if(!online) { /* NOT re-login */ if(cfg.sys_logout[0]) { /* execute system logout event */ lprintf(LOG_DEBUG, "executing logout event: %s", cfg.sys_logout); external(cmdstr(cfg.sys_logout,nulstr,nulstr,NULL),EX_OUTL|EX_OFFLINE); } } if(cfg.logout_mod[0]) { lprintf(LOG_DEBUG, "executing logout module: %s", cfg.logout_mod); exec_bin(cfg.logout_mod,&main_csi); } SAFEPRINTF2(path,"%smsgs/%4.4u.msg",cfg.data_dir,useron.number); if(fexistcase(path) && !flength(path)) /* remove any 0 byte message files */ remove(path); delfiles(cfg.temp_dir,ALLFILES); if(sys_status&SS_USERON) { // Insures the useron actually went through logon()/getmsgptrs() first putmsgptrs(); } if(!REALSYSOP) logofflist(); useron.laston=(time32_t)now; ttoday=useron.ttoday-useron.textra; /* billable time used prev calls */ if(ttoday>=cfg.level_timeperday[useron.level]) i=0; else i=cfg.level_timeperday[useron.level]-ttoday; if(i>cfg.level_timepercall[useron.level]) /* i=amount of time without min */ i=cfg.level_timepercall[useron.level]; j=(int)(now-starttime)/60; /* j=billable time online in min */ if(i<0) i=0; if(j<0) j=0; if(useron.min && j>i) { j-=i; /* j=time to deduct from min */ SAFEPRINTF(str,"Minute Adjustment: %d",-j); logline(">>",str); if(useron.min>(ulong)j) useron.min-=j; else useron.min=0L; putuserdec32(useron.number, USER_MIN, useron.min); } if(timeleft>0 && starttime-logontime>0) /* extra time */ useron.textra+=(ushort)((starttime-logontime)/60); putuserdec32(useron.number, USER_TEXTRA, useron.textra); putuserdatetime(useron.number, USER_NS_TIME, last_ns_time); logoutuserdat(&cfg, &useron, now, logontime); getusrsubs(); getusrdirs(); if(usrgrps>0) putuserstr(useron.number, USER_CURSUB, cfg.sub[usrsub[curgrp][cursub[curgrp]]]->code); if(usrlibs>0) putuserstr(useron.number, USER_CURDIR, cfg.dir[usrdir[curlib][curdir[curlib]]]->code); hhmmtostr(&cfg,&tm,str); SAFECAT(str," "); if(sys_status&SS_USERON) { char ulb[64]; char dlb[64]; safe_snprintf(tmp,sizeof(tmp),"T:%3u R:%3u P:%3u E:%3u F:%3u " "U:%4s %u D:%4s %u" ,(uint)(now-logontime)/60,posts_read,logon_posts ,logon_emails,logon_fbacks ,byte_estimate_to_str(logon_ulb, ulb, sizeof(ulb), 1024, /* precision: */logon_ulb > 1024*1024) ,logon_uls ,byte_estimate_to_str(logon_dlb, dlb, sizeof(dlb), 1024, /* precision: */logon_dlb > 1024*1204) ,logon_dls); } else SAFEPRINTF(tmp,"T:%3u sec",(uint)(now-answertime)); SAFECAT(str,tmp); SAFECAT(str,"\r\n"); logline("@-",str); sys_status&=~SS_USERON; answertime=now; // In case we're re-logging on #ifdef _WIN32 if(startup->sound.logout[0] && !sound_muted(&cfg)) PlaySound(startup->sound.logout, NULL, SND_ASYNC|SND_FILENAME); #endif mqtt_user_logout(mqtt, &client, logontime); lprintf(LOG_DEBUG, "logout completed"); } /****************************************************************************/ /* Detailed usage stats for each logon */ /****************************************************************************/ bool sbbs_t::logofflist() { char str[256]; int file; struct tm tm, tm_now; if(localtime_r(&now,&tm_now)==NULL) return false; if(localtime_r(&logontime,&tm)==NULL) return false; SAFEPRINTF4(str,"%slogs/%2.2d%2.2d%2.2d.lol",cfg.logs_dir,tm.tm_mon+1,tm.tm_mday ,TM_YEAR(tm.tm_year)); if((file=nopen(str,O_WRONLY|O_CREAT|O_APPEND))==-1) { errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT|O_APPEND); return false; } safe_snprintf(str,sizeof(str),"%-*.*s %-2u %-8.8s %2.2u:%2.2u %2.2u:%2.2u %3u %2u %2u %2u %2u " "%2u %2u\r\n",LEN_ALIAS,LEN_ALIAS,useron.alias,cfg.node_num,connection ,tm.tm_hour,tm.tm_min,tm_now.tm_hour,tm_now.tm_min ,(int)(now-logontime)/60,posts_read,logon_posts,logon_emails ,logon_fbacks,logon_uls,logon_dls); int wr = write(file,str,strlen(str)); close(file); return wr == (int)strlen(str); }