#include "Accessdb.h" #include "Hostdb.h" #include "TcpSocket.h" #include "Multicast.h" #include "HttpServer.h" #include "Facebook.h" static bool printCalendars ( SafeBuf &sb , time_t startDate ) ; static bool printCalendar ( SafeBuf &sb , long showDay, long showMonth, long showYear ) ; Accessdb g_accessdb; static void handleRequestaa ( UdpSlot *slot , long niceness ) ; bool Accessdb::registerHandler ( ) { // . register ourselves with the udp server // . it calls our callback when it receives a msg of type 0x0A if ( ! g_udpServer.registerHandler ( 0xaa, handleRequestaa )) return false; return true; } bool Accessdb::init ( ) { long maxTreeMem = 3000000; // g_conf.m_accessdbMaxTreeMem; long nodeSize = sizeof(AccessRec)+8+12+4 + sizeof(collnum_t); // . We take a snapshot of g_stats every minute. // . Each sample struct taken from g_stats ranges from 1k - 2k // after compression depending on the state of the // all errors arrays. uint32_t maxTreeNodes = maxTreeMem / nodeSize; // assume low niceness m_niceness = 0; m_msg4InUse = false; // make the rec cache 0 bytes, cuz we are just using page cache now if ( ! m_rdb.init ( g_hostdb.m_dir , // working directory "accessdb" , // dbname true , // dedup keys 4+8 , // fixed data size (ip+fbid) 2,//g_conf.m_accessdbMinFilesToMerge , maxTreeMem , maxTreeNodes , true , // balance tree? 0 , // m_accessdbMaxCchMem 0 ,// maxCacheNodes , false , // use half keys? false , // cache from disk? NULL , // page cache pointer false , // is titledb? false , // preload cache? sizeof(key128_t) , // key size false , // bias disk page cache? true )) // iscollectionless? syncdb,facebookdb,... return false; // add the base since it is a collectionless rdb return m_rdb.addColl ( NULL ); } // Make sure we need this function. // main.cpp currently uses the addColl from m_rdb bool Accessdb::addColl ( char *coll, bool doVerify ) { if ( ! m_rdb.addColl ( coll ) ) return false; return true; } //static Msg4 s_msg4; //static s_msg4InUse = false; void addedAccessRecWrapper ( void *state ) { g_accessdb.m_msg4InUse = false; } key128_t Accessdb::makeKey1 ( long long now, long long widgetId64 ) { key128_t ak; ak.n1 = now; ak.n0 = widgetId64; // make it a positive key ak.n0 <<= 1; ak.n0 |= 0x01; return ak; } key128_t Accessdb::makeKey2 ( long long now, long long widgetId64 ) { key128_t ak; // indicate its the 2nd type of key by setting high bit ak.n1 = widgetId64 | 0x8000000000000000LL; ak.n0 = now; // make it a positive key ak.n0 <<= 1; ak.n0 |= 0x01; return ak; } // . store two entries: // . time64|widgetid64|ip|fbid // . widgetid64|time64|ip|fbid // . storing group is based on the lower bits of the time64 bool Accessdb::addAccess ( HttpRequest *hr , long ip ) { long long now = gettimeofdayInMillisecondsGlobalNoCore(); long long fbId = hr->getLongLongFromCookie("fbid",0LL); long long widgetId64 = hr->getLongLongFromCookie("widgetid",0LL); // not if widgetid is bogus if ( widgetId64 & 0x8000000000000000LL ) return true; // or now can't have this top bit set. we use that as indicator! if ( now & 0x8000000000000000LL ) { char *xx=NULL;*xx=0; } // log if this is a problem! we need to know it so we can fix if ( m_msg4InUse ) { log("access: msg4 is in use!!!!"); return true; } // do not keep this on the stack in case the msg4 blocks! //AccessRec arec[2]; m_arec[0].m_key128 = g_accessdb.makeKey1(now,widgetId64); m_arec[0].m_ip = ip; m_arec[0].m_fbId = fbId; m_arec[1].m_key128 = g_accessdb.makeKey2(now,widgetId64); m_arec[1].m_ip = ip; m_arec[1].m_fbId = fbId; //log("msg4: timestamp = %llu",now); // this has like a 1 second delay before it flushes so you might // not see you current access until that passes if ( ! m_msg4InUse && ! m_msg4.addMetaList ( (char *)&m_arec[0] , (long)2*sizeof(AccessRec), (collnum_t)0, // collnum NULL, // state addedAccessRecWrapper, 0, // niceness RDB_ACCESSDB ) ) // if this blocked, mark it as in use m_msg4InUse = true; return true; } class MsgaaRequest { public: long m_startDate; long long m_widgetId; long m_minRecSizes; }; class Stateaa { public: long m_replies; long m_requests; long m_errno; TcpSocket *m_socket; Msgfb m_msgfb; MsgaaRequest m_request; // for allocating usually like 32 multicasts long m_numMulticasts; Multicast m_mcasts[MAX_HOSTS]; }; static void gotMulticastReplyWrapperaa ( void *state , void *state2 ); static void gotFBUserInfoWrapper ( void *state ); static bool gotFBUserInfo ( Stateaa *st ) ; // . returns false if blocked, true otherwise // . sets errno on error // . make a web page displaying the config of this host // . call g_httpServer.sendDynamicPage() to send it bool sendPageAccount ( TcpSocket *s , HttpRequest *r ) { Stateaa *st ; try { st = new (Stateaa); } catch ( ... ) { g_errno = ENOMEM; log("access: new(%i): %s", sizeof(Stateaa),mstrerror(g_errno)); g_httpServer.sendErrorReply ( s, 500, mstrerror(g_errno) ); return true; } mnew ( st , sizeof(Stateaa) , "Stateaa" ); // reset counts st->m_errno = 0; st->m_replies = 0; st->m_requests = 0; st->m_socket = s; // get startdate long startDate = r->getLong("sd",0); if ( startDate == 0 ) startDate = getTimeGlobalNoCore() - 7*86400; // get widgetid long long widgetId = r->getLongLong("widgetid",0); // set request st->m_request.m_startDate = startDate; st->m_request.m_widgetId = widgetId; st->m_request.m_minRecSizes = 10000; // first get facebook rec if ( ! st->m_msgfb.getFacebookUserInfo ( r , st->m_socket, NULL,//coll, st , "", // redirpath gotFBUserInfoWrapper, 0 )) // niceness return false; // got it return gotFBUserInfo ( st ); } void gotFBUserInfoWrapper ( void *state ) { Stateaa *st = (Stateaa *)state; if ( ! gotFBUserInfo ( st ) ) return; // there is no callback to call, it must have sent an http reply } // from PageEvents.cpp bool printLoginScript ( SafeBuf &sb , char *redirUrl = NULL ) ; bool printHtmlHeader ( SafeBuf &sb , char *title , bool printPrimaryDiv , SearchInput *si , bool staticPage ); bool printBlackBar(SafeBuf &sb,Msgfb *msgfb,char *page,long ip,bool printLogo, bool igoogle, class State7 *st ); bool printPageTitle ( SafeBuf &sb, char *title ); bool printHtmlTail ( SafeBuf *sb , Msgfb *msgfb , bool printUnsubscribedPopup); bool gotFBUserInfo ( Stateaa *st ) { // if not logged into facebook... if ( ! st->m_msgfb.m_fbId ) { SafeBuf sb; printHtmlHeader ( sb , "My Account", 1 ,NULL,true); char *path = "/account.html"; printBlackBar (sb,&st->m_msgfb, path , st->m_socket->m_ip,1,0, NULL); printPageTitle (sb,"My Account"); sb.safePrintf( "" "" "" "
" "
" "
" "
" "Login with Facebook to access " "your account." "" "
" "
" "" "" "

" "
" "" "" "" ); printHtmlTail ( &sb , &st->m_msgfb, false ); TcpSocket *s = st->m_socket; // nuke the state now mdelete ( st , sizeof(Stateaa) , "Stateaa" ); delete (st); long bufLen = sb.length(); // . send this page // . encapsulates in html header and tail // . make a Mime g_httpServer.sendDynamicPage ( s, sb.getBufStart(), bufLen ); return true; } // if widgetid not explicitly given use facebook id of the user if ( st->m_request.m_widgetId == 0 ) st->m_request.m_widgetId = st->m_msgfb.m_fbId; char *request = (char *)&st->m_request; long requestSize = sizeof(MsgaaRequest); // send the request to every host in the network for ( long i = 0 ; i < g_hostdb.getNumGroups() ; i++ ) { // count send outs st->m_requests++; // get group id unsigned long gid = g_hostdb.getGroupId ( i ); // get the multicast for this group, shortcut Multicast *m = &st->m_mcasts[i]; // send it out if ( ! m->send ( request , requestSize, 0xaa , // msgType 0xaa false , // does multicast own request? gid , // group + offset false , // send to whole group? 0 , // key is passed on startKey st , // state data NULL , // state data gotMulticastReplyWrapperaa , 30 , // timeout in seconds (was 30) 0 , // niceness , false, // realtimeudp? -1 , // first host to try NULL , // reply buf 0 , // reply buf max size true , // free reply buf? true , // do disk load balancing? 0,//maxCacheAge , 0 , // *(key_t *)cacheKey , RDB_ACCESSDB , -1 ) ) { // minrecsizes log("accessdb: multicast send failed"); st->m_errno = g_errno; m->reset(); st->m_replies++; } } // return false if blocked if ( st->m_replies < st->m_requests ) return false; // we did not block gotMulticastReplyWrapperaa ( st , NULL ); // i guess no blocking? return true; } void gotMulticastReplyWrapperaa ( void *state , void *state2 ) { Stateaa *st = (Stateaa *)state; // count it st->m_replies++; // return if awaiting more replies if ( st->m_replies < st->m_requests ) return; key128_t sk; key128_t ek; sk.setMin(); ek.setMax(); char *startKey = (char *)&sk; char *endKey = (char *)&ek; long ks = sizeof(key128_t); long ds = sizeof(AccessRec) - ks; // ptrs to fbrecs that have your widgetid SafeBuf ptrBuf; // store page into here SafeBuf sb; // each mutlicast reply is a list RdbList lists[MAX_HOSTS]; RdbList *plists[MAX_HOSTS]; char *m_freeMe [MAX_HOSTS]; long m_freeMeSize[MAX_HOSTS]; long numLists = g_hostdb.getNumGroups(); for ( long i = 0 ; i < numLists ; i++ ) { // get the multicast for this group, shortcut Multicast *m = &st->m_mcasts[i]; // get the reply long replySize; long replyMaxSize; bool freeReply; char *reply = m->getBestReply ( &replySize , &replyMaxSize , &freeReply ); // save it for freeing if ( freeReply ) { m_freeMe [i] = reply; m_freeMeSize [i] = replyMaxSize; } else { m_freeMe[i] = NULL; } // first is size of the first list char *p = reply; char *pend = p + replySize; long firstListSize = *(long *)p; p += 4; lists[i].set ( p, firstListSize, p, firstListSize, startKey, endKey, ds, false, false, ks); plists[i] = &lists[i]; // a facebook list follows that p += firstListSize; long secondListSize = pend - p; // point to each facebook rec RdbList fblist; fblist.set ( p, secondListSize, p, secondListSize, startKey, endKey, -1, // fixeddatasize false,// owndata? false, // usehalfkeys? sizeof(key96_t) ); // facebookdb keysize // scan list to add ptrs for ( ; ! fblist.isExhausted() ; fblist.skipCurrentRec() ) { // point to it FBRec *fbrec = (FBRec *)fblist.getCurrentRec(); ptrBuf.pushLong((long)fbrec); } } // print the header from PageEvents.cpp printHtmlHeader ( sb , "My Account", 1 ,NULL,true); char *path = "/account.html"; printBlackBar ( sb , &st->m_msgfb, path , st->m_socket->m_ip,1,0,NULL); printPageTitle (sb,"My Account"); sb.safePrintf("
"); //long long widgetId = st->m_request.m_widgetId; /* sb.safePrintf("
" "
" "" "" //"
" // " //"" //"" "
" "" "" "Displaying traffic data for the widget of id " "%llu" "" "" "
" "
" "
" "
" , widgetId , widgetId ); */ // widgetmaster table sb.safePrintf("" "" "" "" "" "" "" "" "
" "
" "" "Personal Info" "" "
Your Name%s
Your Facebook ID" "%lli" "
Your Widget ID" "%lli" "

" ,st->m_msgfb.m_fbrecPtr->ptr_name ,st->m_msgfb.m_fbId ,st->m_msgfb.m_fbId ,st->m_msgfb.m_fbId ,st->m_msgfb.m_fbId ); // print out a calendar table of dates... // really you can just pick a start time and that is when we // start reading from. // but also be able to select a month... // maybe print 5 calendars, with the one in the middle // being the current month. printCalendars ( sb , st->m_request.m_startDate ); // display table of facebook converts widgetmaster did sb.safePrintf("" "" "" "" "" "" "" "" "" ); FBRec **ppp = (FBRec **)ptrBuf.getBufStart(); long n = ptrBuf.length() / 4; for ( long i = 0 ; i < n ; i++ ) { FBRec *fbrec = ppp[i]; // skip if its you! you can't visit your own widget... if ( fbrec->m_fbId == fbrec->m_originatingWidgetId ) continue; struct tm *timeStruct = gmtime ( &fbrec->m_firstFacebookLogin); char time[256]; strftime ( time , 256 , "%b %e %T %Y", timeStruct ); float cash = 0.00; cash = 1.00; sb.safePrintf("" "" "" "" "" "" "" "" ,time // fbrec->m_firstFacebookLogin ,iptoa(fbrec->m_lastLoginIP) ,"US" ,fbrec->m_fbId ,fbrec->m_fbId ,fbrec->m_originatingWidgetId ,cash ); } sb.safePrintf("
" "
" "" "Event Guru Logins" "" "
Login Time (UTC)Last Login IPCountryFacebook IDWidget IDPayout*
%s%s%s" "%llu%llu$%.02f

"); sb.safePrintf("
"); long minRecSizes = st->m_request.m_minRecSizes; // merge them together into a single list RdbList final; // owndata must be true for merge_r to work! final.set ( NULL,0,NULL,0,startKey,endKey,ds,true,false,ks); final.prepareForMerge( plists,numLists,minRecSizes); final.merge_r ( plists , numLists , startKey, endKey , 10000 , true , // remove neg? RDB_ACCESSDB, NULL, NULL, NULL, false, 0 ); // niceness // // print out first 100 // // table headers sb.safePrintf ( "" "" "" "" "" "" "" "" ); final.resetListPtr(); for ( ; ! final.isExhausted() ; final.skipCurrentRecord() ) { char *rec = final.getCurrentRec(); AccessRec *ar = (AccessRec *)rec; unsigned long long timestamp = ar->m_key128.n1; unsigned long long widgetId = ar->m_key128.n0; // overrun the delbit widgetId >>= 1; // swap them? we do two different types of lookups. // if widgetid is 1, which means *any* then the starttime // leads, otherwise the widgetid leads. if ( timestamp & 0x8000000000000000LL ) { timestamp &= 0x7fffffffffffffffLL; long long tmp = widgetId; widgetId = timestamp; timestamp = tmp; } long timestamp32 = timestamp / 1000; struct tm *timeStruct = gmtime ( ×tamp32 ); char time[256]; strftime ( time , 256 , "%b %e %T %Y", timeStruct ); sb.safePrintf("" //"" "" "" "" "" "" , widgetId ); } // end table sb.safePrintf ( "
" "
" "" "Event Guru Hits" "" "
" "
User Access Time (UTC)User IPUser Facebook IDWidget ID
%llu%s%s" , time , iptoa(ar->m_ip) ); if ( ar->m_fbId ) sb.safePrintf("%llu" "" , ar->m_fbId , ar->m_fbId ); else sb.safePrintf("%llu" , ar->m_fbId ); sb.safePrintf ( "%llu

" ); printHtmlTail ( &sb , &st->m_msgfb, false ); // free the multicast replies here for ( long i = 0 ; i < numLists ; i++ ) { if ( ! m_freeMe[i] ) continue; mfree ( m_freeMe[i] ,m_freeMeSize[i],"acmcrep"); } // make a cookie in case they just logged in through this page! // otherwise the login doesn't "stick" SafeBuf cb; if ( st->m_msgfb.m_fbId ) cb.safePrintf("Set-Cookie: fbid=%llu;\r\n", st->m_msgfb.m_fbId); char *cookiePtr = NULL; if ( cb.length() ) cookiePtr = cb.getBufStart(); TcpSocket *s = st->m_socket; // nuke the state now mdelete ( st , sizeof(Stateaa) , "Stateaa" ); delete (st); long bufLen = sb.length(); // . send this page // . encapsulates in html header and tail // . make a Mime g_httpServer.sendDynamicPage ( s, sb.getBufStart(), bufLen , 0 , // cachetime in secs false , // postreply? "text/html", // content type -1, // http status -1->200 cookiePtr, "utf-8" ); } ///////////////////////////////////////// // // HANDLER FUNCTION // ///////////////////////////////////////// class Stateab { public: long m_startDate; long long m_widgetId; long m_minRecSizes; UdpSlot *m_slot; long m_niceness; Msg5 m_msg5; RdbList m_accessList; RdbList m_facebookList; key96_t m_fbstartKey; // store facebook recs in here that match widgetid SafeBuf m_retBuf; }; static void gotAccessListWrapper ( void *state , RdbList *list, Msg5 *msg5 ) ; void handleRequestaa ( UdpSlot *slot , long niceness ) { Stateab *st ; try { st = new (Stateab); } catch ( ... ) { g_errno = ENOMEM; log("Stateab: new(%i): %s",sizeof(Stateab),mstrerror(g_errno)); g_udpServer.sendErrorReply ( slot, g_errno ); return; } mnew ( st , sizeof(Stateab) , "Stateab" ); // . read in beginning at start key using msg5. // . limit to provided widget id MsgaaRequest *req = (MsgaaRequest *)slot->m_readBuf; // save widgetid, startdate, minrecsizes, etc. st->m_startDate = req->m_startDate; st->m_widgetId = req->m_widgetId; st->m_minRecSizes = req->m_minRecSizes; st->m_slot = slot; st->m_niceness = niceness; // shortcut long long wgid = req->m_widgetId; // convert into milliseconds long long timestamp = ((long long)req->m_startDate) * 1000; // back 7 days from now if this is zero if ( req->m_startDate == 0 ) { timestamp = gettimeofdayInMillisecondsGlobalNoCore(); // back 7 days from now timestamp -= 7*86400*1000; } long long longTime = 86400LL*365LL*1000LL*10; // 10 years in millisecs // use the 2nd type of key... those have widgetid first and then // the timestamp key128_t startKey = g_accessdb.makeKey2 ( timestamp ,wgid ); key128_t endKey = g_accessdb.makeKey2 ( timestamp + longTime, wgid); // widget id of 0 means ANY widget if ( wgid == 0 ) { startKey = g_accessdb.makeKey1(timestamp , 0 ); endKey = g_accessdb.makeKey1(timestamp + longTime, 0 ); } // lookup accessdb records from that time going forward if ( ! st->m_msg5.getList ( RDB_ACCESSDB , "", // m_coll , &st->m_accessList , &startKey , &endKey , st->m_minRecSizes , true , // includeTree false , // add to cache? 0 , // max cache age 0 , // startFileNum , -1 , // numFiles , st , // state gotAccessListWrapper , // callback st->m_niceness , false )) // err correction? return; // we got it without blocking gotAccessListWrapper ( st , NULL, NULL ); } static bool scanLoop ( Stateab *st ) ; static void sendListsBack ( Stateab *state ); void gotAccessListWrapper ( void *state , RdbList *list, Msg5 *msg5 ) { Stateab *st = (Stateab *)state; // store size of this list since we will be adding the // facebook into m_retBuf right after. (firstListSize) st->m_retBuf.pushLong ( st->m_accessList.m_listSize ); // now store list from what we read into retBuf st->m_retBuf.safeMemcpy ( st->m_accessList.getList() , st->m_accessList.m_listSize ); // reset start key for scan st->m_fbstartKey.setMin(); // . returns false if blocked, true otherwise // . scan ALL facebookdb for recs originating from this widgetid if ( ! scanLoop( st ) ) return; // it didn't block sendListsBack ( st ); } static void gotScanListWrapper ( void *state, RdbList *list , Msg5 *msg5 ) ; static void gotScanList ( Stateab *st ) ; // . scan facebookdb and get every facebookid, and couple it with the // time we gotta send the email // . sort by that in emailTree // . re-scan facebookdb every few hours in case of new entries or if // someone updates their email // . i would also call addToEmailTree if a new facebookdb rec comes in. // perhaps do that from Rdb.cpp? // . returns false if blocked true otherwise bool scanLoop ( Stateab *st ) { key96_t endKey ; endKey.setMax(); // get a meg at a time long minRecSizes = 1024*1024; key96_t oldk; oldk.setMin(); loop: // use msg5 to get the list, should ALWAYS block since no threads if ( ! st->m_msg5.getList ( RDB_FACEBOOKDB , "", // m_coll , &st->m_facebookList , &st->m_fbstartKey , &endKey , minRecSizes , true , // includeTree false , // add to cache? 0 , // max cache age 0 , // startFileNum , -1 , // numFiles , st , // state gotScanListWrapper , // callback st->m_niceness , false )) // err correction? // return false if we blocked return false; // stuff the m_emailTree with some data based on m_list gotScanList ( st ); // if something, get more if ( ! st->m_facebookList.isEmpty() ) goto loop; // i guess we did not block? return true; } void gotScanListWrapper ( void *state, RdbList *list , Msg5 *msg5 ) { // use this Stateab *st = (Stateab *)state; // this never blocks gotScanList ( st ); // and resume the loop. return if it blocked. if ( ! scanLoop ( st ) ) return; // alldone sendListsBack ( st ); } void gotScanList ( Stateab *st ) { //long now = getTimeGlobal(); //long dayStart = now - ( now % 86400 ); if ( st->m_facebookList.isEmpty() ) return; // loop over entries in list for ( st->m_facebookList.resetListPtr() ; ! st->m_facebookList.isExhausted() ; st->m_facebookList.skipCurrentRecord() ) { // get it char *drec = st->m_facebookList.getCurrentRec(); long drecSize = st->m_facebookList.getCurrentRecSize(); // sanity check. delete key? if ( (drec[0] & 0x01) == 0x00 ) continue; // get widgetit FBRec *fr = (FBRec *)drec; // but allow widget id of 0 through if its set to 1 long long wgid = fr->m_originatingWidgetId; if ( wgid == 0 ) wgid = 1; // a zero widgetid means any! a one means came from no widget. if ( st->m_widgetId && wgid != st->m_widgetId ) continue; // copy over to same list st->m_retBuf.safeMemcpy ( drec , drecSize ); } st->m_fbstartKey = *(key96_t *)st->m_facebookList.getLastKey(); st->m_fbstartKey += (unsigned long) 1; // watch out for wrap around //if ( startKey < *(key96_t *)list.getLastKey() ) return; } void sendListsBack ( Stateab *st ) { char *data = st->m_retBuf.getBufStart(); long dataSize = st->m_retBuf.length(); long allocSize = st->m_retBuf.getCapacity(); // release it so udpserver can free it st->m_retBuf.detachBuf(); // save slot UdpSlot *slot = st->m_slot; // nuke the state now mdelete ( st , sizeof(Stateab) , "stateab" ); delete (st); g_udpServer.sendReply_ass ( data , dataSize , data , allocSize , slot , 60 , NULL , NULL , // doneSending_ass , -1 , -1 , true ); } /////////// // // the calendar for the page accessdb // ////////// // . print 5 calendars in a row, with current one in the middle // . all are in UTC bool printCalendars ( SafeBuf &sb , long startDate ) { // parse it up long now = startDate; // getTimeGlobalNoCore(); struct tm *timeStruct = gmtime ( &now ); // get month number (0 to 11) long thisMonth = timeStruct->tm_mon; // 0-11 long thisDay = timeStruct->tm_mday; long thisYear = timeStruct->tm_year + 1900; long prevMonth1; long prevYear1; long postMonth1; long postYear1; prevMonth1 = thisMonth - 1; prevYear1 = thisYear; if ( prevMonth1 < 0 ) { prevMonth1 += 12; prevYear1--; } postMonth1 = thisMonth + 1; postYear1 = thisYear; if ( postMonth1 >= 12 ) { postMonth1 -= 12; prevYear1++; } sb.safePrintf("" "" ); sb.safePrintf("
" "
" "" "Select a Date" "" "
" ); printCalendar ( sb , 0 , prevMonth1 , prevYear1 ); sb.safePrintf(""); printCalendar ( sb , thisDay , thisMonth , thisYear ); sb.safePrintf(""); printCalendar ( sb , 0 , postMonth1 , postYear1 ); sb.safePrintf("

"); return true; } // "now" is from the msg40 we used bool printCalendar ( SafeBuf &sb , long showDay, long showMonth, long showYear ) { // compute show dow struct tm tss; tss.tm_mon = showMonth; tss.tm_mday = 1; tss.tm_sec = 0; tss.tm_min = 0; tss.tm_hour = 0; tss.tm_year = showYear - 1900; time_t mt = mktime ( &tss ); // now get dow of the first day of this month struct tm *tt = gmtime ( &mt ); long firstDayOfWeek = tt->tm_wday; // 0-6 // this is that day time_t startDate = mt; // get today's month/day/year long now = getTimeGlobalNoCore(); struct tm *nt = gmtime ( &now ); long nowDay = nt->tm_mday; long nowMonth = nt->tm_mon; long nowYear = nt->tm_year + 1900; // we got days per month. leap year? long daysInMonth = getNumDaysInMonth ( showMonth , showYear ); // prev month abbr char *nextStr, *str, *prevStr; if ( showMonth == 0 ) { prevStr = "Dec"; str="Jan"; nextStr = "Feb"; } if ( showMonth == 1 ) { prevStr = "Jan"; str="Feb"; nextStr = "Mar"; } if ( showMonth == 2 ) { prevStr = "Feb"; str="Mar"; nextStr = "Apr"; } if ( showMonth == 3 ) { prevStr = "Mar"; str="Apr"; nextStr = "May"; } if ( showMonth == 4 ) { prevStr = "Apr"; str="May"; nextStr = "Jun"; } if ( showMonth == 5 ) { prevStr = "May"; str="Jun"; nextStr = "Jul"; } if ( showMonth == 6 ) { prevStr = "Jun"; str="Jul"; nextStr = "Aug"; } if ( showMonth == 7 ) { prevStr = "Jul"; str="Aug"; nextStr = "Sep"; } if ( showMonth == 8 ) { prevStr = "Aug"; str="Sep"; nextStr = "Oct"; } if ( showMonth == 9 ) { prevStr = "Sep"; str="Oct"; nextStr = "Nov"; } if ( showMonth == 10 ) { prevStr = "Oct"; str="Nov"; nextStr = "Dec"; } if ( showMonth == 11 ) { prevStr = "Nov"; str="Dec"; nextStr = "Jan"; } long prevYear = showYear; long prevMonth = showMonth - 1; if ( prevMonth < 0 ) { prevMonth = 11; prevYear--; } long nextYear = showYear; long nextMonth = showMonth + 1; if ( nextMonth >= 12 ) { nextMonth = 0; nextYear++; } // print print out calendar header sb.safePrintf("" "" "" "" "" "\n" "" "" "" "" "" "" "" "" "\n" // cal now for prev month //, prevYear //, prevMonth //, prevStr , str , showYear // cal now for next month //, nextYear //, nextMonth //, nextStr ); bool printed = false; long count = 1; bool showCursor = true; // print out days of the week header for ( long i = 0 ; i < 35 ; i++ ) { if ( i % 7 == 0 ) sb.safePrintf(""); // is it today? if ( count == nowDay && showMonth == nowMonth && showYear == nowYear && i>= firstDayOfWeek && showCursor ) sb.safePrintf("" , startDate + (count-1)*86400 , count ); count++; } else sb.safePrintf(">"); if ( (i+1) % 7 == 0 ) sb.safePrintf("\n"); } sb.safePrintf("
" //"" //"%s" //"" "
%s %li
" //"" //"%s" //"" "
SMTWTFS
= firstDayOfWeek ) sb.safePrintf("= firstDayOfWeek || printed) && count <= daysInMonth ) { printed = true; //lo clockSet2=getYearMonthStart(showYear,showMonth+1); // add day to it //clockSet2 += (count-1) * 86400; // clockSet is in utc... //clockSet2 -= timeZoneOffset * 3600; // end the /* sb.safePrintf(" onclick=\"" // set hidden tag clockset val // set all to gray if not yellow // set clicked to red if not yellow "top.window.href='/traffic?sd=%lu';\">" , startDate + (count-1)*86400 ); */ sb.safePrintf(">%li" "
"); return true; }