/* Copyright (c) 2017, Computer History Museum All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ //depot/projects/eudora/mac/Current/adwin.c#127 - edit change 17326 (text) #ifdef AD_WINDOW #include "adwin.h" #include "buildversion.h" /* Window for ads */ #define FILE_NUM 122 #pragma segment ADWIN #ifdef DEBUG #define LOGADS #define kAdScheduleAccelerator 60 // show ads 60 times faster #endif //#define LOGINFO // Sends ads server date/time and user id #ifdef LOGADS #define LogAd(s) { Str255 sLog; DoLogAds(s); } #else #define LogAd(s) #endif #define kMaxDownloads 4 // Don't request more than this many ads at one time #define kPlayListVersion 22 // Playlist version we support #define kReqInverval 1 // Check for a new playlist after this many days #define kFaceTimeQuota 60 // default facetime quota minutes #define kRerunInterval 7 // default return interval, days #define kPurgeAdDays 90 // Purge cached ads after this many days #define kCheckCoverAd 5 // Check every 5 seconds to see if ad has been covered #define kCoveredAdDelay 60 // After we've discovered that the ad window is covered, wait before alerting user #define kMaxObscuredPixels 500 // Nag user if this many pixels are obscured in ad window #define kSaveStateSecs 15*60 // Save playlist state every 15 minutes #define kMaxReqInterval 14*24 // Don't wait more than 14 days before downloading a new playlist #define kMinRetryInterval (4*3600) // Wait at least 4 hrs between playlist requests #define kAdWidth 144 #define kAdHeight 144 #define kHistLength 30 // For how many do we report info on ads? #define kReqInterval 24 #define kSchedType kLinear #define kPlayListServerId -1 // Pseudo server id indicating playlist, not ad #define kAdDragBarHeight 8 #define kNewAdTypingTicks 300 // Don't display a new ad unless the user hasn't typed for this many ticks #define kAdDownloadAttemptTicks 7200 // Don't attempt to download ads if we've tried to recently #define kCheckSumSize 16 #define kQuotaSlop (60) typedef long StrOffset; // For accessing strings at end of playlist data typedef enum { vaCheckNormalAdOnly=1<<0, vaCheckDayMax=1<<1 ,vaCheckShowForMax=1<<2, vaCheckRerun=1<<3 } ValidAdFlags; // PlayList data. Saved in one resource for each playlist #define kPlayListVers 4 #define kPlayListResType 'Eupl' enum SchedType { kLinear, kRandom }; typedef enum { kIsButton=1,kIsSponsor,kIsRunout} AdType; enum MixType { kMix,kBlock}; // Increment kPlayListVers if AdInfo or PlayList is changed typedef struct { AdType type; long showFor; // How many seconds to display? long dayMax; // Maximum times to display per day long showForMax; // Maximum seconds to show total StrOffset srcURL; // URL to ad (c-string) long blackBefore; // Seconds of black before ad long blackAfter; // Seconds of black before ad uLong startDate; // Don't start before this time uLong endDate; // Don't display after this time AdId adId; StrOffset title; // Title (used in link window) StrOffset clickURL; // URL to where we go when we click unsigned char checksum[kCheckSumSize]; } AdInfo, *AdInfoPtr; typedef struct PlayListStruct PlayList, *PlayListPtr, **PlayListHandle; struct PlayListStruct { long version; PlayListHandle next; long strOffset; // Offset to beginning of strings StrOffset playListIDString; StrOffset clickBase; long playListId; // hash of playlist id string enum MixType mixType; enum SchedType blockScheduleType; long totalShowMax; // Sum of showMax values for all ads short numAds; AdInfo ads[]; }; // Play state data - what is the state of ad display, saved in a resource // Increment kStateVersion if AdState or PlayState is changed #define kStateVersion 12 #define kStateResType 'Euas' #define kStateResId 1000 #define kPastryResId 1001 // State info for ads. Use for playlist state also. typedef struct { AdId adId; // Ad Id, or playlist ID if adId.server == -1 long shownToday; // How many times displayed today long shownTotal; // How many total seconds displayed for this ad uLong lastDisplayTime; // When did we last display the ad? Used for aging. Boolean unused; // was: flushed Boolean deleted; // Toolbar button deleted? } AdState, *AdStatePtr; typedef struct { short version; // version # for this data (kStateVersion) long showCur; // How long will current ad be shown short numAds; // Client info long adWidth; long adHeight; long reqInterval; long faceTimeQuota; enum SchedType schedType; short histLength; long curPlayList; AdId curAd; DateTimeRec today; DateTimeRec getNextPlaylist; short unused; short adsNotFetched; // How many ads have we been unable to fetch into cache? short errCode; // Are we unable to get ads? Boolean outOfAds; Boolean dirty; long faceTimeToday; // Today's total facetime long faceTimeWeek[7]; // Facetime for past 7 days AdState stateAds[]; } PlayState11, *PlayState11Ptr, **PlayState11Handle; typedef struct { short version; // version # for this data (kStateVersion) long showCur; // How long will current ad be shown short numAds; // Client info long adWidth; long adHeight; long reqInterval; long faceTimeQuota; enum SchedType schedType; short histLength; long curPlayList; AdId curAd; DateTimeRec today; DateTimeRec getNextPlaylist; short unused; short adsNotFetched; // How many ads have we been unable to fetch into cache? short errCode; // Are we unable to get ads? Boolean outOfAds; Boolean dirty; long faceTimeToday; // Today's total facetime long faceTimeWeek[7]; // Facetime for past 7 days long adFaceTimeToday; // Today's facetime for ads long rerunInterval; long spare[19]; // Leave some room, fer goodness' sake! AdState stateAds[]; } PlayState, *PlayStatePtr, **PlayStateHandle; // List of ads yet to be downloaded typedef struct DownloadList **DownloadListHandle; typedef struct DownloadList { DownloadListHandle next; Handle url; AdId adId; Boolean display; AdType adType; } DownloadList; #define REMOVE_AND_DESTROY(hPlayList) \ do { \ LL_Remove(gPlayListQueue,hPlayList,(PlayListHandle)); \ RemoveResource((Handle)hPlayList); \ if (gPlayList==hPlayList) gPlayList = gPlayListQueue; \ ZapHandle(hPlayList); \ UpdateResFile(SettingsRefN); \ } while (0) /************************************************************************ * globals ************************************************************************/ long Secret3 = 0x65B9DE33; char PlayListURL[] = "\phttp://adserver.eudora.com/adjoin/playlists"; long Secret1 = 0x6867B92E; StringPtr PlayListKeywords[] = { // Client update response parameters "\pclientInfo", "\preqInterval","\phistLength","\ppastry","\pflush","\pscheduleAlgorithm", "\pwidth","\pheight","\pfaceTimeQuota","\prerunInterval", "\pfault","\pfaultCode","\pfaultString", // Nag "\pnag","\plevel", // Reset "\preset", // Client update request parameters "\pclientUpdate", "\puserAgent","\plocale","\pclientMode","\pfaceTime","\pfaceTimeLeft","\pdistributorID","\pscreen","\pprofile", "\pfaceTimeUsedToday","\pplayListVersion", #ifdef LOGINFO "\puserID","\pdateAndTime", #endif // Playlist parameters "\pplaylist", "\pplaylistID","\pmixAlgorighm","\pblockScheduleAlgorithm","\pclickBase", // Ad entry parameters "\pentry", "\pdayMax","\pblackBefore","\pblackAfter", "\pshowFor","\pstartDT","\pendDT", "\pshowForMax","\pstartTime","\pendTime", "\psrc","\padID", "\ptitle","\purl", // Attributes "\plinear","\prandom", // scheduleAlgorithm "\pmix","\pblock", // mixAlgorithm "\pisButton","\pisSponsor","\pisRunout", // entry type "\pentity","\pid", // for flush "\pchecksum", // checksum for entry.src "" }; enum { kClientInfoTkn, kReqIntervalTkn,kHistLengthTkn,kPastryTkn,kFlushTkn,kScheduleTkn, kWidthTkn,kHeightTkn,kFaceTimeQuotaTkn,kRerunIntervalTkn, kFaultTkn,kFaultCodeTkn,kFaultStringTkn, kNagTkn,kLevelTkn, kResetTkn, kClientUpdateTkn, kUserAgentTkn,kLocaleTkn,kClientModeTkn,kFaceTimeTkn,kFaceTimeLeftTkn,kDistIDTkn,kScreenTkn,kUserProfileTkn, kFaceTimeUsedTodayTkn,kPlayListVersionTkn, #ifdef LOGINFO kUserId,kDateAndTime, #endif kPlayListTkn, kPlayListIdTkn,kMixAlgorithmTkn,kBlockScheduleAlgorithmTkn,kClickBaseTkn, kEntryTkn, kDayMaxTkn,kBlackBeforeTkn,kBlackAfterTkn, kShowForTkn,kStartDateTkn,kEndDateTkn, kShowForMax,kStartTime,kEndTime, kSrcTkn,kAdIdTkn, kTitleTkn,kClickTkn, // Attributes kLinearTkn,kRandomTkn, kMixTkn,kBlockTkn, kIsButtonTkn,kIsSponsorTkn,kIsRunoutTkn, kEntityTkn,kIdTkn, kCheckSum, }; // Global variables MyWindowPtr AdWin; PETEHandle ADpte; PlayListHandle gPlayList; PlayStateHandle gPlayState; PlayListHandle gPlayListQueue; uLong gNextAdSecs; long Secret2 = 0xA30580D2; short gDisplayMinutes = 1; long gBlackTime,gNextBlackTime; FMBHandle gFaceMeasure; FMBHandle gGlobalFaceMeasure; Boolean gAdWinCanClose,gPLDownloadInProgress; OSErr gDownloadErrCode; AdId gWasCurAd; Boolean gDoNextAd; Boolean gPlayListDownloadFail; DownloadListHandle gAdDownloadList; short gDownloadCount; StringHandle gDownloadErrString; short gAdsMenuAdsStart; TBAdHandle gTBAds; uLong gPlayListFetchGMT; PicHandle gSponsorAdPic; // "co-branding spot" AdId gSponsorAdId; AdId gInsertAdId; Boolean gInsertAd; short gAdFetchCount; #ifdef DEBUG enum { dbNewPlaylist=1,dbReloadPlaylist,dbDumpPlaylists,dbDiv,dbDefaultPLServer }; char sPlayListFileName[] = "\peudora ads"; #endif /************************************************************************ * prototypes ************************************************************************/ void AdClick(MyWindowPtr win,EventRecord *event); void NextAd(long secsDisplayed); void NewAd(short adIdx); void InsertAd(short adIdx); static OSErr BuildPlayList(UHandle text); static void DisposePlaylists(void); static void CleanupAdCache(Boolean withPrejuidice); #ifdef LOGADS static void DoLogAds(StringPtr sLog); #endif static Boolean AdClose(MyWindowPtr win); Boolean AdPosition(Boolean save,MyWindowPtr win); static void AdTransition (MyWindowPtr win, UserStateType oldState, UserStateType newState); static AdInfoPtr FindAd(AdId adId,short *idx,PlayListHandle *playList); static AdInfoPtr FindCurrentAd(void); static AdInfoPtr FindCurrentAdIdx(short *idx); static void SavePlayStatus(void); static void SavePastry(Ptr pPastry,long len); static void AdCursor(Point mouse); static void NewDayCheck(Boolean withPrejuidice); static void PreFetchAds(void); static OSErr GetAdSpec(FSSpecPtr spec, AdId adId); static void ParseAdId(StringPtr sToken, AdId *pAdId); static void AdUpdate(MyWindowPtr win); static void ShowBlackTimeImage(void); static Boolean ValidAd(PlayListHandle hPlayList,short adIdx,uLong curTime,ValidAdFlags flags); static OSErr LoadPlaylists(void); void RequestPlaylist(void); void NewPlaylist(FSSpecPtr spec,StringPtr checkSum); #ifdef DEBUG static void SetupAdDebugMenu(); void DumpPlaylists(Boolean fullDisplay); static Boolean DumpClose(MyWindowPtr win); static void AccuDump(StringPtr s); static void AccuDumpTime(StringPtr sName, uLong seconds); #endif static StringPtr GetPlayListString(PlayListHandle hPlayList,StrOffset offset,StringPtr s); static Handle GetPlayListURL(PlayListHandle hPlayList,StrOffset offset); static StringPtr GetPlayListURLString(PlayListHandle hPlayList,StrOffset offset,StringPtr s); static AdStatePtr FindAdState(AdId adId,short *idx); static AdStatePtr FindCurrentAdState(void); static OSErr AddStringToHandle(StrOffset *offset,AccuPtr a,StringPtr s); static PlayListHandle FindPlayList(long playListId); static void AddStates(PlayListHandle hPlayList, AccuPtr aState,Boolean init); static AdStatePtr FindPlayListState(PlayListHandle hPlayList); static void SetCurrentPlayList(void); static AdInfoPtr FindAdType(AdType adType,short *idx,PlayListHandle *playList); static AdInfoPtr FindAdTypeLo(AdType adType,short startIdx,PlayListHandle startPlayList,short *idx,PlayListHandle *playList); static void Flush(TokenInfo *pInfo); static Boolean QueueAdDownload(Handle url, AdId adId, AdType adType); static void CheckDownloadList(void); static void FinishedAdDownload(long refCon,OSErr err,DownloadInfo *info); static void PrunePlayState(Handle hKeepAdList); static OSErr AdDragHandler(MyWindowPtr win,DragTrackingMessage which,DragReference drag); Boolean AdCoveredRect(Rect *r); static void SetupToolbarAds(void); static void MakeAdIconFromFile(FSSpecPtr spec, Handle iconSuite); static void MakeAdIcon(PlayListHandle hPlayList,StrOffset srcURL,AdId adId,Handle iconSuite,Boolean downloadOK); static void SetTBAdIcon(AdId adId); static void DeleteAd(AdId adId); long TotalFaceTimeLeft(void); void RollGlobalFacetime(void); OSErr PastryIsStale(Handle pastry); static OSErr SetupAdState(Boolean reinit); static void ResetAdState(void); void LogPlayStatus(void); OSErr SetupForDisplay(Ptr data,long dataLen,unsigned char checksum[kCheckSumSize],Boolean check); static void ZapSponsorAd(void); static OSErr CheckAd(AdId adId,Handle hData,FSSpec *spec); static void InitAdPte(void); static void ResetAdSchedule(void); static Boolean IsChecksumFailAgain(AdId adId); OSErr GetAdFolderSpec(FSSpecPtr spec); OSErr HandleNag(long level,PStr text); OSErr HandleUserProfile(PStr text); OSErr HandleClientMode(PStr text); OSErr ReadNoAdsRec(NoAdsAuxPtr noAds); OSErr SaveNoAdsRec(NoAdsAuxPtr noAds); OSErr TestWriteToAdFolder(void); /************************************************************************ * OpenAdWindow - open ad window ************************************************************************/ void OpenAdWindow(void) { if (IsAdwareMode()) { if (!AdWin) { // Make sure we have an ad cache folder FSSpec spec; GetAdFolderSpec(&spec); // Make sure we have an ad folder if (AdWin=GetNewMyWindowWithClass(AD_WIND,nil,nil,BehindModal,False,False,AD_WIN, // Give the ad window a very high float level under OS X so that // nothing can obscure it // Don't float so high for OS 9. It causes problems with dialogs HaveOSX() ? kHelpWindowClass : kToolbarWindowClass)) { WindowPtr AdWinWP; if (AdWinWP = GetMyWindowWindowPtr (AdWin)) { SetPort_(GetWindowPort(AdWinWP)); MySetThemeWindowBackground(AdWin,kThemeActiveModelessDialogBackgroundBrush,False); AdWin->position = AdPosition; AdWin->isRunt = true; AdWin->windowType = kDockable; AdWin->click = AdClick; AdWin->close = AdClose; AdWin->transition = AdTransition; AdWin->cursor = AdCursor; AdWin->update = AdUpdate; AdWin->drag = AdDragHandler; InitAdPte(); gFaceMeasure = NewFaceMeasure(); FaceMeasureBegin(gFaceMeasure); gBlackTime = -1; // Negative value means display black until an ad shows up ShowMyWindow(AdWinWP); PositionDockedWindow(AdWinWP); #ifdef DEBUG SetupAdDebugMenu(); #endif ReadNoAdsRec(&NoAdsRec); // Get playlists if (!LoadPlaylists()) { if (!gPlayListQueue && !TCPWillDial(true)) // Don't connect the net on first launch! RequestPlaylist(); SizeAdWin(); } else // Can't set up playlists CloseAdWindow(); NewDayCheck(false); // Check for new day before displaying first ad } } } if (!AdWin || !ADpte) DieWithError(INIT_ADWIN_ERR,MemError()?MemError():ResError()?ResError():1); } else if (IsFreeMode()) { LoadPlaylists(); // Load playlists so we can check for sponsor ad } SetupToolbarAds(); // Make sure toolbar ads are installed (or removed) SetupSponsorAd(); } /************************************************************************ * CloseAdWindow - close ad window ************************************************************************/ void CloseAdWindow(void) { if (AdWin) { // if an ad was up, record that it's now gone. if (gPlayList && (gWasCurAd.server || gWasCurAd.ad)) { AuditAdClose(gWasCurAd.server,gWasCurAd.ad); gWasCurAd.server = gWasCurAd.ad = 0; } gAdWinCanClose = true; CloseMyWindow(GetMyWindowWindowPtr(AdWin)); gAdWinCanClose = false; AdWin = nil; } RollGlobalFacetime();// save faceTimeToday gNextAdSecs = 0; gBlackTime = gNextBlackTime = 0; SavePlayStatus(); DisposePlaylists(); DisposeFaceMeasure(gFaceMeasure); gFaceMeasure = nil; if (gPlayState) { ReleaseResource((Handle)gPlayState); gPlayState = nil; } } /************************************************************************ * AdShutdown - Eudora is shutting down ************************************************************************/ void AdShutdown(void) { SavePlayStatus(); } /************************************************************************ * AdPosition - position the ad window ************************************************************************/ Boolean AdPosition(Boolean save,MyWindowPtr win) { WindowPtr winWP = GetMyWindowWindowPtr (win); Boolean did = PositionPrefsTitle(save,win); short spot; if (!save && !did && winWP) { // we have no idea where the ad window should go. Position // it in the lower-right GDHandle gd = MyGetMainDevice(); Rect r; Rect rCovered; SizeWindow(winWP,GetRLong(AD_WINDOW_SIZE_GUESS_X),GetRLong(AD_WINDOW_SIZE_GUESS_Y),false); for (spot=0;spot<5;spot++) { r = (*gd)->gdRect; if (gd==GetMainDevice()) r.top += GetMBarHeight(); switch (spot) { case 0: case 4: // Bottom left r.right = r.left + WindowWi(winWP); r.top = r.bottom - WindowHi(winWP); break; case 1: // Bottom right, left of trash r.left = r.right - WindowWi(winWP) - 48; r.right -= 48; r.top = r.bottom - WindowHi(winWP); break; case 2: // Bottom right, above trash r.left = r.right - WindowWi(winWP); r.top = r.bottom - WindowHi(winWP) - 48; r.bottom -= 48; break; case 3: // Upper right, left of disk r.left = r.right - WindowWi(winWP) - 48; r.right -= 48; r.bottom = r.top + WindowHi(winWP); break; } utl_RestoreWindowPos(winWP,&r,false,1,0,LeftRimWidth(winWP),(void*)FigureZoom,(void*)DefPosition); PositionDockedWindow(winWP); // Make sure positioned correctly if docked // Show it now so we can see if it's covered ShowHide(winWP,true); StashStructure(win); if (AdCoveredRect(&rCovered)) { if (rCovered.top > (r.top+r.bottom)/2 && (spot==0 || spot==2)) { // Rat doot; try moving it up r.bottom = rCovered.top; r.top = r.bottom - WindowHi(winWP); utl_RestoreWindowPos(winWP,&r,false,1,0,LeftRimWidth(winWP),(void*)FigureZoom,(void*)DefPosition); PositionDockedWindow(winWP); // Make sure positioned correctly if docked } } if (!AdCoveredRect(&rCovered)) break; } if (AdCoveredRect(&rCovered)) ComposeStdAlert(Note,SORRY_AD_COVERED); did = true; } return did; } /************************************************************************ * AdClose - close ad window? ************************************************************************/ static Boolean AdClose(MyWindowPtr win) { // Normally we won't want anything to close the ad window // It is allowed only when CloseAdWindow is called return gAdWinCanClose; } /************************************************************************ * AdTransition - transition the window between user states ************************************************************************/ static void AdTransition (MyWindowPtr win, UserStateType oldState, UserStateType newState) { if (!AdwareMode (newState)) CloseAdWindow (); else if (!AdwareMode (oldState) && AdwareMode (newState)) OpenAdWindow (); } /************************************************************************ * AdClick - click in ad window ************************************************************************/ void AdClick(MyWindowPtr win,EventRecord *event) { Point pt; pt = event->where; GlobalToLocal(&pt); // Drag window if no drag bar with click and drag, or inside dragbar if ((PrefIsSet(PREF_NO_ADWIN_DRAG_BAR) && MyWaitMouseMoved(event->where,True)) || (!PtInRect(pt,&win->contR))) { // In drag bar DragMyWindow(GetMyWindowWindowPtr(win),event->where); } else if (!gBlackTime) AdUserClick((*gPlayState)->curAd); } /************************************************************************ * AdUserClick - handle a click on an ad ************************************************************************/ void AdUserClick(AdId adId) { Str255 s; AdInfoPtr pAd; PlayListHandle hPlayList; if ((pAd = FindAd(adId,nil,&hPlayList)) && hPlayList) { OSErr err = noErr; StrOffset clickURL = pAd->clickURL; if ((*hPlayList)->clickBase) { // Need to append ad URL to this click base URL Str255 sURL; AdURLStringsRec adStrings; Str32 sAdId; GetPlayListURLString(hPlayList,clickURL,sURL); ComposeString(sAdId,"\p%d.%d",adId.server,adId.ad); adStrings.adID = sAdId; adStrings.destination = sURL; GetPlayListURLString(hPlayList,(*hPlayList)->clickBase,s); err = OpenAdwareURLStr(GetNagState(),s,actionClickThrough,clickThruQuery,(long)&adStrings); } else { Handle url = GetPlayListURL(hPlayList,clickURL); if (url) { char *urlCStr = LDRef(url); short urlLen = strlen(urlCStr); if (fnfErr==(err=OpenLocalURLPtr(urlCStr,urlLen,nil,nil,false))) if ((err=ParseProtocolFromURLPtr(urlCStr,urlLen,s))==noErr) err = OpenOtherURLPtr(s,urlCStr,urlLen); ZapHandle(url); } AdWasClicked(adId,err); } } AuditAdHit(adId.server,adId.ad); } /************************************************************************ * AdWinIdle - idle time handler for ad window ************************************************************************/ void AdWinIdle(void) { static uLong nextIdle,nextCheckCover,nextReportCovered,nextSaveStatus; static uLong lastAdAttempt = 0; static Boolean sponsorAdInited = false; WindowPtr AdWinWP; uLong faceTime, totalTime, seconds; DateTimeRec dt; if (IsPayMode()) return; // Not in adware or free mode if (gInsertAd) { short adIdx; if (FindAd(gInsertAdId,&adIdx,&gPlayList)) InsertAd(adIdx); gInsertAd = false; } if (nextIdle && TickCount()0) // are we connected to the network right now somehow? { // have we tried downloading ads recently? if (TickCount()-lastAdAttempt>kAdDownloadAttemptTicks) { // go fetch the ads that we need AdCheckingMail(); lastAdAttempt = TickCount(); } } CheckDownloadList(); // Any ads to download? seconds = LocalDateTime(); if (nextSaveStatus) { if (seconds >= nextSaveStatus) { SavePlayStatus(); nextSaveStatus = seconds + kSaveStateSecs; } } else nextSaveStatus = seconds + kSaveStateSecs; if (IsFreeMode() && !sponsorAdInited) { SetupToolbarAds(); // We haven't set up the sponsor ad yet sponsorAdInited = true; } // Has playlist expired? GetTime(&dt); if (TimeCompare(&dt,&(*gPlayState)->getNextPlaylist)>=0) { // Need a new playlist DateTimeRec nextPlaylist; // Set next time for download in an hour so we don't // continually request playlists SecondsToDate(LocalDateTime()+60L*60L,&nextPlaylist); (*gPlayState)->getNextPlaylist = nextPlaylist; RequestPlaylist(); } if (!AdWin) return; // Ad window is not currently open. We may be in free mode. if (InBG || FaceMeasureReport(gFaceMeasure,&faceTime,nil,nil,&totalTime)) return; // End of face-to-black time? Don't start ad display unless user is looking. if (gBlackTime>0) { if (totalTime >= gBlackTime) { // Fade-to-black time is over. Can draw again LogAd(ComposeString(sLog,"\pShow for: %d",gNextAdSecs)); gBlackTime = 0; FaceMeasureReset(gFaceMeasure); InvalContent(AdWin); } } // Time for a new ad? else if ((TickCount()-TypingTicks>kNewAdTypingTicks) && ((gNextAdSecs && faceTime >= gNextAdSecs) || gDoNextAd)) { gDoNextAd = false; NextAd(gNextAdSecs); } if (!AdWin) return; // Ad window has been closed if (AdWinWP = GetMyWindowWindowPtr (AdWin)) if (!IsWindowVisible (AdWinWP)) { // Someone's trying to pull a fast one. Make sure ad window stays visible. ShowHide(AdWinWP,true); StashStructure(AdWin); } #ifndef I_HATE_THE_BOX if (seconds >= nextCheckCover) { WindowPtr theWindow; Rect rSect; // Search for any windows in front of ad window that overlap // If so, bring ad window in front for (theWindow = FrontWindow (); theWindow && theWindow != AdWinWP; theWindow = GetNextWindow (theWindow)) { Rect rStrucWin,rStrucAdWin; if (SectRect(GetStructureRgnBounds(theWindow,&rStrucWin),GetStructureRgnBounds(AdWinWP,&rStrucAdWin),&rSect)) { if (!ModalWindow) BringToFront(AdWinWP); break; } } // Make sure there aren't any system floating windows in front if (AdCoveredRect(nil)) { // Ad window is obscured. Start time to ask user to uncover. if (!nextReportCovered) nextReportCovered = seconds + kCoveredAdDelay; else if (seconds >= nextReportCovered) { extern ModalFilterUPP DlgFilterUPP; // Time to tell the user to uncover the ad window nextReportCovered = 0; Nag (AD_OBSCURED_DLOG, nil, HitMeHitMeHitMe, DlgFilterUPP, false, 0); } } else nextReportCovered = 0; nextCheckCover = seconds + kCheckCoverAd; } #endif #ifdef DEBUG { static short fastAds; if (BUG4 != fastAds) { if (BUG4) { // Catch on to change to fast ads quickly if (gNextAdSecs) gDoNextAd = true; gBlackTime = 0; } fastAds = BUG4; } } #endif } /************************************************************************ * AdCoveredRect - Find the covered part of the ad ************************************************************************/ Boolean AdCoveredRect(Rect *r) { CGrafPtr AdWinPort = GetMyWindowCGrafPtr (AdWin); RgnHandle rgn = NewRgn(); Rect rCovered; Zero(rCovered); if (rgn && AdWinPort && !ModalWindow) // don't check if modal window up! { Rect rPortRect,rRgnBounds; // The visRgn should cover this entire region RectRgn(rgn,GetPortBounds(AdWinPort,&rPortRect)); // It's OK if a few pixels around the edges are obscured InsetRgn(rgn,5,5); // Subtract out the visRgn. Anything left over is an obscured area DiffRgn(rgn,MyGetPortVisibleRegion(AdWinPort),rgn); // Globalize it PushGWorld(); SetPort(AdWinPort); GlobalizeRgn(rgn); PopGWorld(); // Record rectangle rCovered = *GetRegionBounds(rgn,&rRgnBounds); DisposeRgn(rgn); } if (r) *r = rCovered; return (RectHi(rCovered) * RectWi(rCovered) > kMaxObscuredPixels); } /************************************************************************ * AdCursor - set the cursor ************************************************************************/ static void AdCursor(Point mouse) { #ifdef DEBUG if (CurrentModifiers()&controlKey) { SetMyCursor(MENU_CURS); return; } #endif if (!gBlackTime && PtInRect(mouse,&AdWin->contR)) SetMyCursor(POINTER_FINGER); else // In drag bar SetMyCursor(arrowCursor); } /************************************************************************ * AdUpdate - update ad window ************************************************************************/ static void AdUpdate(MyWindowPtr win) { CGrafPtr AdWinPort = GetMyWindowCGrafPtr (AdWin); Rect r,rPortBounds; PixPatHandle hPixPat; if (!PrefIsSet(PREF_NO_ADWIN_DRAG_BAR)) { // Draw the drag bar // Start with some borders r = *GetPortBounds(AdWinPort,&rPortBounds); SetForeGrey(0); // black if (RectHi(r)*2 <= RectWi(r)) { // Put title bar on the side of this short, wide window r.right = kAdDragBarHeight-1; MoveTo(kAdDragBarHeight-1,0); LineTo(kAdDragBarHeight-1,r.bottom); } else { // Put title bar on top of normal window, r.bottom = kAdDragBarHeight-1; MoveTo(0,kAdDragBarHeight-1); LineTo(r.right,kAdDragBarHeight-1); } // Now the pattern if (hPixPat = GetPixPat(DRAG_BAR_PIXPAT)) { //InsetRect(&r,1,1); //r.bottom--; FillCRect(&r,hPixPat); DisposePixPat(hPixPat); } } if (gBlackTime || !gNextAdSecs) { ShowBlackTimeImage(); SetEmptyVisRgn(AdWinPort); // Don't allow pete handle to be updated } } /************************************************************************ * AdDragHandler - we don't accept any drags ************************************************************************/ static OSErr AdDragHandler(MyWindowPtr win,DragTrackingMessage which,DragReference drag) { // We don't wany any window highlighting during a drag to the ad window // Return an error, but not handlerNotFoundErr because PantyTrack will // still do a PeteDrag return -1; } /************************************************************************ * SavePlayStatus - save play status resource ************************************************************************/ static void SavePlayStatus(void) { if (gPlayState && (*gPlayState)->dirty) { (*gPlayState)->dirty = false; ChangedResource((Handle)gPlayState); MyUpdateResFile(SettingsRefN); #ifdef DEBUG LogPlayStatus(); #endif } } /************************************************************************ * LogPlayStatus - log gPlayState ************************************************************************/ void LogPlayStatus(void) { if (gPlayState) { ComposeLogS(LOG_PLIST,nil,"\pversion %d showCur %d numAds %d", (*gPlayState)->version, (*gPlayState)->showCur, (*gPlayState)->numAds); ComposeLogS(LOG_PLIST,nil,"\padWidth %d adHeight %d reqInterval %d faceTimeQuota %dm", (*gPlayState)->adWidth, (*gPlayState)->adHeight, (*gPlayState)->reqInterval, (*gPlayState)->faceTimeQuota/60); ComposeLogS(LOG_PLIST,nil,"\pschedType %d histLength %d curPlayList %x curAd %d.%d", (*gPlayState)->schedType, (*gPlayState)->histLength, (*gPlayState)->curPlayList, (*gPlayState)->curAd.server,(*gPlayState)->curAd.ad); ComposeLogS(LOG_PLIST,nil,"\padFetchCount %d adsNotFetched %d errCode %d outOfAds %d", gAdFetchCount, (*gPlayState)->adsNotFetched, (*gPlayState)->errCode, (*gPlayState)->outOfAds); ComposeLogS(LOG_PLIST,nil,"\pdirty %d adDay %d totalDay %d S %d M %d T %d W %d R %d F %d S %d (mins)", (*gPlayState)->dirty, (*gPlayState)->adFaceTimeToday/60, (*gPlayState)->faceTimeToday/60, (*gPlayState)->faceTimeWeek[0]/60, (*gPlayState)->faceTimeWeek[1]/60, (*gPlayState)->faceTimeWeek[2]/60, (*gPlayState)->faceTimeWeek[3]/60, (*gPlayState)->faceTimeWeek[4]/60, (*gPlayState)->faceTimeWeek[5]/60, (*gPlayState)->faceTimeWeek[6]/60); } } /************************************************************************ * SizeAdWin - resize ad window ************************************************************************/ void SizeAdWin(void) { WindowPtr AdWinWP = GetMyWindowWindowPtr (AdWin); if (AdWin && AdWinWP && gPlayState) { Rect r; short dragBarHt=0,dragBarWd=0; if (!PrefIsSet(PREF_NO_ADWIN_DRAG_BAR)) { if ((*gPlayState)->adHeight*2 > (*gPlayState)->adWidth) dragBarHt = kAdDragBarHeight; else // Title bar on left side of window dragBarWd = kAdDragBarHeight; } SetRect(&r,0,0,(*gPlayState)->adWidth+dragBarWd,(*gPlayState)->adHeight+dragBarHt); SizeWindow(AdWinWP,r.right,r.bottom,true); SetPrefLong(AD_WINDOW_SIZE_GUESS_X,RectWi(r)); SetPrefLong(AD_WINDOW_SIZE_GUESS_Y,RectHi(r)); r.left=dragBarWd; r.top=dragBarHt; AdWin->contR = r; PeteDidResize(ADpte,&r); PositionDockedWindow(AdWinWP); // Make sure positioned correctly if docked } } /********************************************************************** * FinishedPlayListDownload - we have finished downloading a play list **********************************************************************/ static void FinishedPlayListDownload(long refCon,OSErr err,DownloadInfo *info) { FSSpec spec; gPLDownloadInProgress = false; #ifdef DEBUG ComposeLogS(LOG_PLIST,nil,"\pPlayList Fetch Done: %d",err); #endif if (!err) { // Use new playlist NewPlaylist(&info->spec,info->checksum); gPlayListDownloadFail = false; } else { // Download failed. DateTimeRec nextPlaylist; gPlayListDownloadFail = true; // Let's try again in an hour. Will also try on each mail check if (gPlayState) { SecondsToDate(LocalDateTime()+60L*60L,&nextPlaylist); (*gPlayState)->getNextPlaylist = nextPlaylist; } } spec = info->spec; #ifdef DEBUG if (RunType!=Production) { FSSpec keepSpec; // Let's keep playlist file for debugging // Move file from temporary folder to ad folder GetAdFolderSpec(&keepSpec); FSpCatMove(&spec,&keepSpec); SubFolderSpec(AD_FOLDER_NAME,&keepSpec); PCopy(keepSpec.name,sPlayListFileName); FSpDelete(&keepSpec); PCopy(keepSpec.name,spec.name); FSpRename(&keepSpec,sPlayListFileName); } else #endif FSpDelete(&info->spec); gDownloadErrCode = err; if (gPlayState) { (*gPlayState)->errCode = err; } } /********************************************************************** * FinishedAdDownload - we have finished downloading an ad **********************************************************************/ static void FinishedAdDownload(long refCon,OSErr err,DownloadInfo *info) { FSSpec spec,tempSpec; OSErr getAdErr; DownloadListHandle hDownload = (DownloadListHandle)refCon; Handle url; if (!hDownload) return; if (gPlayState) { gAdFetchCount--; getAdErr = GetAdSpec(&tempSpec, (*hDownload)->adId); if (!err) err = FindTemporaryFolder(Root.vRef,Root.dirId,&tempSpec.parID,&tempSpec.vRefNum); if (!err) { if (!getAdErr) { if (FileTypeOf(&tempSpec)=='TEXT') { // Is HTML. Download images. } // Move file from temporary folder to ad folder if (!(err = GetAdFolderSpec(&spec))) err = FSpCatMove(&tempSpec,&spec); if (!err) { switch ((*hDownload)->adType) { case kIsButton: SetTBAdIcon((*hDownload)->adId); break; case kIsSponsor: SetupSponsorAd(); break; default: if (AdWin && (*hDownload)->display) { // This ad should be displayed right away gInsertAd = true; gInsertAdId = (*hDownload)->adId; } break; } } } } if (err) { // Unable to download/install this ad (*gPlayState)->adsNotFetched++; if (!getAdErr) FSpDelete(&tempSpec); if ((*hDownload)->display) { // We were supposed to display this ad gDoNextAd = true; // Set up to display next ad } } (*gPlayState)->errCode = err; } gDownloadErrCode = err; gDownloadCount--; url = (*hDownload)->url; ZapHandle(url); ZapHandle(hDownload); } /********************************************************************** * NewPlaylist - use a new playlist **********************************************************************/ void NewPlaylist(FSSpecPtr spec,StringPtr checkSum) { UHandle text; OSErr err = noErr; unsigned char key[kCheckSumSize]; if (!Snarf(spec,&text,0)) { // validate checksum if we have one if (checkSum) { Hex2Bytes(checkSum+1,32,key); err = SetupForDisplay(LDRef(text),GetHandleSize(text),key,true); } if (!err) err = BuildPlayList(text); ZapHandle(text); if (!err) { SizeAdWin(); gDoNextAd = true; // Set up to display next ad PreFetchAds(); // Load all ads into cache SetupToolbarAds(); SetupSponsorAd(); } if (!(*gPlayState)->faceTimeQuota || (*gPlayState)->faceTimeToday < (*gPlayState)->faceTimeQuota) // Go back to showing regular ads again if factTimeQuota hasn't expired (*gPlayState)->outOfAds = false; if (!gNextAdSecs) gDoNextAd = true; // We were out of ads. Start showing them again } } /********************************************************************** * LoadPlaylists - load and install playlists from playlist resources **********************************************************************/ static OSErr LoadPlaylists(void) { short oldResFile,count; short i; Accumulator aState; OSErr err; if (gPlayListQueue) return noErr; // Already got 'em oldResFile = CurResFile(); UseResFile(SettingsRefN); // Setup ad state data if (err = SetupAdState(false)) return err; // Now get playlists AccuInitWithHandle(&aState,(Handle)gPlayState); count = Count1Resources(kPlayListResType); for(i=1;i<=count;i++) { PlayListHandle hPlayList; AdStatePtr pPlayListState; if (hPlayList = (PlayListHandle)Get1IndResource(kPlayListResType,i)) { #if kPlayListVers == 4 if ((*hPlayList)->version == 3) { // Version 3 may have bad checksums. Let's ignore them. short adIdx; for(adIdx=0;adIdx<(*hPlayList)->numAds;adIdx++) Zero((*hPlayList)->ads[adIdx].checksum); (*hPlayList)->version = kPlayListVers; } #else #error Remove this entire conditional compile directive if version is greater than 4 #endif if ((*hPlayList)->version == kPlayListVers) { long shownTotal = 0; long totalShowMax = 0; long curTime = LocalDateTime(); short adIdx; (*hPlayList)->next = nil; // old value; destroy LL_Queue(gPlayListQueue,hPlayList,(PlayListHandle)); AddStates(hPlayList,&aState,false); pPlayListState = FindPlayListState(hPlayList); for(adIdx=0;adIdx<(*hPlayList)->numAds;adIdx++) if (ValidAd(hPlayList,adIdx,curTime,vaCheckNormalAdOnly+vaCheckShowForMax)) { AdInfo *pAd = &(*hPlayList)->ads[adIdx]; AdStatePtr pAdState = FindAdState(pAd->adId,nil); if (pAdState) { shownTotal += pAdState->shownTotal; totalShowMax += pAd->showForMax; } } (*hPlayList)->totalShowMax = totalShowMax; pPlayListState->shownTotal = shownTotal; } else // Old playlist format. Delete it. RemoveResource((Handle)hPlayList); } } AccuTrim(&aState); // Don't dispose of this accumulator! SetCurrentPlayList(); UseResFile(oldResFile); gDoNextAd = true; // Set up to display first ad #ifdef DEBUG SetupAdDebugMenu(); #endif return noErr; } /********************************************************************** * SetupAdState - setup ad state data **********************************************************************/ static OSErr SetupAdState(Boolean reinit) { short oldResFile; PlayStatePtr pState; uLong stateSize; oldResFile = CurResFile(); UseResFile(SettingsRefN); // Start with play state if (!gPlayState) gPlayState = Get1Resource(kStateResType,kStateResId); if (!gPlayState) { // Create a new play state resource gPlayState = NuHandle(sizeof(PlayState)); if (!gPlayState) return MemError(); AddResource_(gPlayState,kStateResType,kStateResId,""); } #ifdef DEBUG LogPlayStatus(); #endif RollGlobalFacetime();// kick off face time measurement for this session pState = *gPlayState; // Can't use this if it's a wrong version or if it's not // for the current playlist stateSize = sizeof(PlayState) + pState->numAds*sizeof(AdState); if (reinit || pState->version != kStateVersion || stateSize != GetHandleSize((Handle)gPlayState)) { OSErr err = 1; if (kStateVersion==12 && pState->version == 11 && GetHandleSize(gPlayState)==sizeof(PlayState11) + pState->numAds*sizeof(AdState) ) { // We can rebuild him! short sz = sizeof(PlayState); short sz11 = sizeof(PlayState11); short diff = sz-sz11; short szAds = pState->numAds*sizeof(AdState); // resize SetHandleBig(gPlayState,GetHandleSize(gPlayState)+diff); if (!(err=MemError())) { // move ads out of the way of new data BMD(*gPlayState+sz11,*gPlayState+sz,szAds); // zero new data WriteZero(*gPlayState+sz11,diff); // Update the version. Forgot this first time (*gPlayState)->version = kStateVersion; } } // Initialize a new play state if (err) { SetHandleSize((Handle)gPlayState,sizeof(PlayState)); ZeroHandle(gPlayState); pState = *gPlayState; } } if (!pState->version) { // Setup new play state. DateTimeRec today; pState->version = kStateVersion; pState->adWidth = kAdWidth; pState->adHeight = kAdHeight; pState->reqInterval = kReqInverval; pState->histLength = kHistLength; GetTime(&today); (*gPlayState)->today = today; (*gPlayState)->getNextPlaylist = today; } UseResFile(oldResFile); return noErr; } /********************************************************************** * InsertAd - insert the current ad **********************************************************************/ void InsertAd(short adIdx) { WindowPtr AdWinWP = GetMyWindowWindowPtr (AdWin); CGrafPtr AdWinPort = GetWindowPort (AdWinWP); FSSpec spec, *pImageSpec = nil; Str255 sURL,s; AdStatePtr pAdState,pPlayListState; uLong seconds; AdId adId; GetPlayListURLString(gPlayList,(*gPlayList)->ads[adIdx].srcURL,sURL); GetCacheSpec(sURL,&spec,true); // Remove current ad but don't draw to make for a smoother transition // between ads SetEmptyClipRgn(AdWinPort); PeteDelete(ADpte,0,0x7fffffff); if (FileTypeOf(&spec)=='TEXT') { // Text file means HTML document UHandle text; char base[256]; if (!Snarf(&spec,&text,0)) { long tStart = 0; long tOffset = -1; StringPtr spot; // Need to insert base ref. Use directory containing html source if (spot = PRIndex(sURL,'/')) *sURL = spot-sURL; ComposeRString(base,MHTML_INFO_TAG,sURL,"",0,""); Munger(text,0,nil,0,base+1,*base); ComposeString(base,"\p<%r>",htmlXHTMLDir+HTMLDirectiveStrn); Munger(text,0,nil,0,base+1,*base); InsertHTML(text,&tStart,GetHandleSize(text),&tOffset,ADpte,kDontEnsureCR+kNoMargins); ZapHandle(text); } } else { // Graphic file. Insert into pte uLong start; OSErr err; PeteGetTextAndSelection(ADpte,nil,&start,nil); // Current selection start should be zero err = PeteInsertChar(ADpte,-1,' ',nil); ASSERT(err==noErr); // Error in ad window pte if (err == errAECorruptData) { // The pte is corrupted. Let's just get a new one and try it again. PeteDispose(AdWin,ADpte); ADpte = nil; InitAdPte(); err = PeteInsertChar(ADpte,-1,' ',nil); } if (!err) PeteFileGraphicRange(ADpte,start,start+1,&spec,fgDisplayInline+fgCenterInWindow); pImageSpec = &spec; } PeteLock(ADpte,0,0x7fffffff,peModLock|peSelectLock); InfiniteClip(GetWindowPort(AdWinWP)); InvalContent(AdWin); seconds = LocalDateTime(); // If the ad window is open, and there's an ad in in, the old ad was just closed. if (IsWindowVisible (AdWinWP) && (gWasCurAd.server || gWasCurAd.ad)) { AuditAdClose(gWasCurAd.server,gWasCurAd.ad); gWasCurAd.server = gWasCurAd.ad = 0; } if ((pAdState=FindCurrentAdState())) { // Notify link window about ad AdInfoPtr pAd; if (!(*gPlayState)->outOfAds) pAdState->lastDisplayTime = seconds; if (pAd = FindCurrentAd()) { GetPlayListString(gPlayList,pAd->title,s); AddAdToLinkHistory(pAd->adId,GetPlayListURLString(gPlayList,pAd->clickURL,sURL), s, pImageSpec); } } if (!(*gPlayState)->outOfAds && (pPlayListState=FindPlayListState(gPlayList))) pPlayListState->lastDisplayTime = seconds; gWasCurAd = (*gPlayState)->curAd; adId = (*gPlayList)->ads[adIdx].adId; AuditAdOpen(adId.server, adId.ad); // audit the fact that an ad was displayed } /************************************************************************ * NewDayCheck - see if we've gone to a new day ************************************************************************/ static void NewDayCheck(Boolean withPrejuidice) { static short todayMonth, todayDay; DateTimeRec dt; PlayStatePtr pState; GetTime(&dt); if (!todayMonth) { todayMonth = dt.month; todayDay = dt.day; } else if (!AdWin && (dt.month != todayMonth || dt.day != todayDay)) // Need to reopen ad window OpenAdWindow(); if (AdWin && gPlayState) { if (withPrejuidice || DateCompare(&dt,&(*gPlayState)->today)) { // New day short i; AdInfoPtr pAd; RollGlobalFacetime(); // record facetime now AdFailureChecks(&NoAdsRec); SaveNoAdsRec(&NoAdsRec); if ((*gPlayState)->outOfAds || ((pAd=FindCurrentAd()) && pAd->type==kIsRunout)) ResetAdSchedule(); // Start from beginning of ad list // Reset all shownToday times pState=*gPlayState; for(i=0;inumAds;i++) pState->stateAds[i].shownToday = 0; // Set up daily facetime info pState->faceTimeWeek[pState->today.dayOfWeek-1] = pState->faceTimeToday; pState->faceTimeToday = 0; pState->adFaceTimeToday = 0; pState->today.month = dt.month; pState->today.day = dt.day; pState->today.dayOfWeek = dt.dayOfWeek; pState->today.year = dt.year; pState->outOfAds = false; pState->dirty = true; gDoNextAd = true; // New day, new ad } } } /************************************************************************ * ReadNoAdsRec - read the no ads record ************************************************************************/ OSErr ReadNoAdsRec(NoAdsAuxPtr noAds) { TOCHandle outTOC = GetSpecialTOC(OUT); if (outTOC) { BMD(&(*outTOC)->internalUseOnly,noAds,sizeof(*noAds)); return noErr; } return fnfErr; } /************************************************************************ * SaveNoAdsRec - save the no ads record ************************************************************************/ OSErr SaveNoAdsRec(NoAdsAuxPtr noAds) { TOCHandle outTOC = GetSpecialTOC(OUT); if (outTOC) { BMD(noAds,&(*outTOC)->internalUseOnly,sizeof((*outTOC)->internalUseOnly)); TOCSetDirty(outTOC,true); return noErr; } return fnfErr; } /************************************************************************ * AdFailureChecks - check for ad failure ************************************************************************/ OSErr AdFailureChecks(NoAdsAuxPtr noAds) { Boolean wereFailing = noAds->adsAreFailing; Boolean wereSucceeding = noAds->adsAreSucceeding; // roll the mailcheck value noAds->checkedMail = noAds->checkedMailToday; noAds->checkedMailToday = false; // assume life is good noAds->adsAreFailing = false; noAds->adsAreSucceeding = true; // and leave it at that. Ads don't fail anymore, because we are OUTTA HERE! // no more Sponsored mode. No more Eudora business. Hasta la vista, baby. return noErr; #ifndef I_HATE_THE_BOX if (AdWin) { // determine if we're failing or not if (!gPlayState) { // if no playlists, we're failing noAds->adsAreFailing = true; noAds->adsAreSucceeding = false; } else { // did the user use the program significantly? Boolean hadSignificantFacetime = (*gPlayState)->faceTimeToday >= ((*gPlayState)->faceTimeQuota)/2; // is the ad facetime insufficient? Boolean adTimeTooSmall; long adFaceTime = (*gPlayState)->adFaceTimeToday+kQuotaSlop+gNextAdSecs; // half the facetime quota? if (adFaceTime >= (*gPlayState)->faceTimeQuota/2) adTimeTooSmall = false; // half the actual facetime? else if (adFaceTime >= (*gPlayState)->faceTimeToday/2) adTimeTooSmall = false; else // something is wrong... adTimeTooSmall = true; noAds->adsAreFailing = adTimeTooSmall && noAds->checkedMail; noAds->adsAreSucceeding = hadSignificantFacetime && !adTimeTooSmall; } // Adjust our counters if (noAds->adsAreFailing) { if (wereSucceeding) noAds->consecutiveDays = 1; else noAds->consecutiveDays++; if (noAds->deadbeatCounter <= kDeadbeatDays) noAds->deadbeatCounter++; } else if (noAds->adsAreSucceeding) { noAds->deadbeatCounter = MIN(noAds->deadbeatCounter,kDeadbeatDays-1); if (wereFailing) noAds->consecutiveDays = 1; else noAds->consecutiveDays++; if (noAds->consecutiveDays > kAdGracePeriod && noAds->deadbeatCounter>0) { noAds->deadbeatCounter--; noAds->consecutiveDays = 0; } } #ifdef DEBUG ComposeLogS(LOG_PLIST,nil,"\pAreAdsFailing----->: fail %p succ %p gPS %x adFace %d face %d ftq %d",noAds->adsAreFailing?YesStr:NoStr,noAds->adsAreSucceeding?YesStr:NoStr,gPlayState,(*gPlayState)->adFaceTimeToday, (*gPlayState)->faceTimeToday, (*gPlayState)->faceTimeQuota); ComposeLogS(LOG_PLIST,nil,"\pAreAdsFailing----->: checked %p dbc %d cd %d",noAds->checkedMail?YesStr:NoStr,noAds->deadbeatCounter,noAds->consecutiveDays); #endif } #endif return noErr; } /************************************************************************ * NextAd - get the next ad ************************************************************************/ void NextAd(long secsDisplayed) { PlayListHandle hPlayList; AdStatePtr pAdState,pPlayListState; if (AdWin && gPlayListQueue) { uLong curTime = LocalDateTime(); Boolean found = false; // If we're out of ads, we display ads ignoring how long they've already displayed ValidAdFlags vaFlags = (*gPlayState)->outOfAds ? vaCheckNormalAdOnly+vaCheckRerun : vaCheckNormalAdOnly+vaCheckDayMax+vaCheckShowForMax; short adIdx; if (!gPlayList) gPlayList = gPlayListQueue; // Add time to previous ad (if we haven't run out of ads including runout ads) if ((gWasCurAd.server || gWasCurAd.ad) && !(*gPlayState)->outOfAds) { if (pAdState = FindCurrentAdState()) { pAdState->shownToday++; pAdState->shownTotal += secsDisplayed; (*gPlayState)->adFaceTimeToday += secsDisplayed; AuditAdClose(gWasCurAd.server,gWasCurAd.ad); gWasCurAd.server = gWasCurAd.ad = 0; } if (pPlayListState = FindPlayListState(gPlayList)) pPlayListState->shownTotal += secsDisplayed; } (*gPlayState)->showCur = 0; if (!(*gPlayState)->faceTimeQuota || (*gPlayState)->faceTimeToday < (*gPlayState)->faceTimeQuota || (*gPlayState)->outOfAds) { // Find ad to display if ((*gPlayState)->schedType == kRandom) { // Find next ad randomly unsigned short randomCount = 0; Accumulator a; // Build list of valid ads to select from AccuInit(&a); for(hPlayList=gPlayListQueue;hPlayList;hPlayList=(*hPlayList)->next) { for(adIdx=0;adIdx<(*hPlayList)->numAds;adIdx++) { if (ValidAd(hPlayList,adIdx,curTime,vaFlags)) { AccuAddPtr(&a,&(*hPlayList)->ads[adIdx].adId,sizeof(AdId)); randomCount++; } } } if (randomCount) { unsigned short randomValue = ((unsigned short)Random()) % randomCount; FindAd(((AdId *)*a.data)[randomValue],&adIdx,&gPlayList); found = true; } AccuZap(a); } else { // Find next ad sequentially short startAdIdx; short wrapCount; if (FindCurrentAdIdx(&adIdx)) adIdx++; else { // Couldn't find current ad, start with first ad adIdx = 0; gPlayList = gPlayListQueue; } startAdIdx=adIdx-1; hPlayList = gPlayList; wrapCount = 0; do { while(adIdx<(*hPlayList)->numAds && !found) { if (adIdx==startAdIdx && hPlayList==gPlayList) goto NotFound; // Went though all ads. Didn't find any valid ones if (ValidAd(hPlayList,adIdx,curTime,vaFlags)) { // Use it! found = true; gPlayList = hPlayList; } else adIdx++; } if (!found) { hPlayList = (*hPlayList)->next; // Try next playlist if (!hPlayList) { // Last playlist, wrap around to first if (wrapCount) goto NotFound; // We're already gone this route and haven't found a thing wrapCount++; hPlayList = gPlayListQueue; // Wrap around } adIdx = 0; if (startAdIdx<0) startAdIdx = 0; // Couldn't find current ad, started with first ad } } while (!found); } } if (found) { NewAd(adIdx); } else { AdInfoPtr pAd; NotFound: // We are out of ads or have reached our quota if ((*gPlayState)->outOfAds) { // Unlikely this will happen, but we just can't find // any ads we can display. Go to black. ShowBlackTimeImage(); gNextAdSecs = 0; // No more ads until tomorrow gBlackTime = -1; // Negative value means display black until an ad shows up } // Out of regular ads. Do we have a runout ad? else if (pAd = FindAdType(kIsRunout,&adIdx,&gPlayList)) { short showFor = pAd->showFor; NewAd(adIdx); if (pAdState = FindCurrentAdState()) { // Charge runout ad for full display time pAdState->shownToday++; pAdState->shownTotal += showFor; } } else { // No regular ads and no runouts. Let's start showing // regular ads again at no charge. (*gPlayState)->outOfAds = true; gNextAdSecs = 1; // Do this instead of goto top of this function ResetAdSchedule(); // Start from beginning of ad list } } UL(gPlayList); #ifdef DEBUG if (RunType==Debugging && FindTextLo(nil,"\pPlaylist Info")) DumpPlaylists(false); #endif } } /************************************************************************ * ValidAd - can this ad be used? ************************************************************************/ static Boolean ValidAd(PlayListHandle hPlayList,short adIdx,uLong curTime,ValidAdFlags flags) { AdInfo *pAd = &(*hPlayList)->ads[adIdx]; AdStatePtr pAdState; if (pAd->adId.server || pAd->adId.ad) if (pAdState = FindAdState(pAd->adId,nil)) if (!(flags&vaCheckNormalAdOnly) || !pAd->type) if (!(flags&vaCheckDayMax) || !pAd->dayMax || pAdState->shownToday < pAd->dayMax) if (!(flags&vaCheckShowForMax) || !pAd->showForMax || pAdState->shownTotal < pAd->showForMax) if (!(flags&vaCheckRerun) || !(*gPlayState)->rerunInterval || curTime - pAdState->lastDisplayTime < (*gPlayState)->rerunInterval) { // Not done displaying this ad. // See if we are in range if ((pAd->startDate==0 || pAd->startDate <= curTime) && (pAd->endDate==0 || pAd->endDate >= curTime)) return true; } return false; } /************************************************************************ * NewAd - use a new ad ************************************************************************/ static void NewAd(short adIdx) { Str255 sURL; FSSpec spec; AdInfo *pAd = &(*gPlayList)->ads[adIdx]; AdId adId; (*gPlayState)->curAd = pAd->adId; gNextAdSecs = pAd->showFor; (*gPlayState)->showCur = pAd->showFor; adId = pAd->adId; (*gPlayState)->curPlayList = (*gPlayList)->playListId; GetPlayListURLString(gPlayList,pAd->srcURL,sURL); GetCacheSpec(sURL,&spec,true); #ifdef DEBUG LogAd(ComposeString(sLog,"\pLoading: %p",sURL)); #endif gBlackTime = gNextBlackTime; if (gBlackTime < pAd->blackBefore) gBlackTime = pAd->blackBefore; gNextBlackTime = pAd->blackAfter; #ifdef DEBUG if (gBlackTime) { LogAd(ComposeString(sLog,"\pBlack for: %d",gBlackTime)); } else { LogAd(ComposeString(sLog,"\pShow for: %d",gNextAdSecs)); } #endif FaceMeasureReset(gFaceMeasure); if (!FSpExists(&spec)) InsertAd(adIdx); else { DownloadListHandle hDownload; // Don't have ad in cache. Pre-cache all ads PreFetchAds(); // Put our ad first in list and mark it for display for(hDownload=gAdDownloadList;hDownload;hDownload=(*hDownload)->next) { if (SameAd(&adId,&(*hDownload)->adId)) { LL_Remove(gAdDownloadList,hDownload,(DownloadListHandle)); LL_Push(gAdDownloadList,hDownload); (*hDownload)->display = true; break; } } gNextAdSecs += 60; // Give us a minute to download this thing, otherwise go on to the next ad } (*gPlayState)->dirty = true; #ifdef DEBUG if (BUG4) { // Make those ads display much faster if (gNextAdSecs) { gNextAdSecs /= kAdScheduleAccelerator; if (!gNextAdSecs) gNextAdSecs = 1; } if (gBlackTime) { gBlackTime /= kAdScheduleAccelerator; if (!gBlackTime) gBlackTime = 1; } } #endif } /************************************************************************ * RequestPlaylist - download a play list ************************************************************************/ void RequestPlaylist(void) { FSSpec spec; long reference; HTTPinfo httpStuff; Accumulator a; Handle text; Str255 s,s2; Handle hPastry; Rect rScreen; PlayListHandle hPlayList; short i; long faceTimeLeft; OSErr err; unsigned char key[kCheckSumSize]; short depth; if (gPLDownloadInProgress) return; // We're already downloading a playlist RollGlobalFacetime(); // make sure our counters are up-to-date Zero(httpStuff); httpStuff.post = true; PCopy(httpStuff.sContentType,"\ptext/xml"); PCopy(httpStuff.sMessageType,"\pCall"); // Build request info AccuInit(&a); XMLNoIndent(); AccuAddStr(&a,"\p"); AccuAddCRLF(&a); AccuAddCRLF(&a); AccuAddTagLine(&a,PlayListKeywords[kClientUpdateTkn],false); XMLIncIndent(); // userAgent AccuAddXMLObject(&a,PlayListKeywords[kUserAgentTkn],ComposeString(s,"\pEudora/%d.%d.%d.%d (MacOS)",MAJOR_VERSION,MINOR_VERSION,INC_VERSION,BUILD_VERSION)); // locale AccuAddXMLWithAttr(&a,PlayListKeywords[kLocaleTkn],ComposeString(s,"\planguage=\"%p\"",GetLanguageCode(s2)),true); // clientMode where 0=adware, 1=light, 2=paid AccuAddXMLObject(&a,PlayListKeywords[kClientModeTkn],IsAdwareMode() ? "\p0" : IsPayMode() ? "\p2" : IsProfileDeadbeatUser() ? "\p3" : "\p1"); // playlists and ad id's if (gPlayListQueue) { PlayListHandle hNextPlayList; Handle hKeepAdList = NuHandleClear((*gPlayState)->numAds); for(hPlayList=gPlayListQueue;hPlayList;hPlayList=hNextPlayList) { uLong curTime = LocalDateTime(); uLong expiredTime = curTime - (*gPlayState)->histLength*24*60*60; short i; Boolean foundKeeper = false; GetPlayListString(hPlayList,(*hPlayList)->playListIDString,s2); AccuAddXMLWithAttr(&a,PlayListKeywords[kPlayListTkn],ComposeString(s,"\pid=\"%p\"",s2),false); // playlist and id // Do all ads XMLIncIndent(); for(i=0;i<(*hPlayList)->numAds;i++) { long active = ValidAd(hPlayList,i,curTime,vaCheckShowForMax)?1:0; AdId adId = (*hPlayList)->ads[i].adId; short adStateIdx; AdStatePtr pAdState; // Report only ads that aren't too old if (adId.server || adId.ad) if ((pAdState = FindAdState(adId,&adStateIdx)) && pAdState->lastDisplayTime > expiredTime && (!(*hPlayList)->ads[i].endDate || (*hPlayList)->ads[i].endDate > expiredTime)) { foundKeeper = true; if (hKeepAdList) (*hKeepAdList)[adStateIdx]=true; ComposeString(s,"\pid=\"%d.%d\" active=\"%d\"",adId.server,adId.ad,active); switch ((*hPlayList)->ads[i].type) { case kIsButton: PCat(s,"\p isButton=\"1\""); if ((*gPlayState)->stateAds[adStateIdx].deleted) // deleted toolbar button PCat(s,"\p deleted=\"1\""); break; case kIsSponsor: PCat(s,"\p isSponsor=\"1\""); break; case kIsRunout: PCat(s,"\p isRunout=\"1\""); break; } AccuAddXMLWithAttr(&a,PlayListKeywords[kEntryTkn],s,true); } #ifdef I_EVER_FIX_THE_ENDDATE_BUG else { if (pAdState) ASSERT(!(*hPlayList)->ads[i].endDate || pAdState->lastDisplayTime < (*hPlayList)->ads[i].endDate); } #endif } hNextPlayList=(*hPlayList)->next; if (!foundKeeper) { // There are no ads in this playlist that are less than "histLength" days old // Get rid of it REMOVE_AND_DESTROY(hPlayList); } else { AdId adId; short playListStateIdx; AdStatePtr pPlayListState; adId.server = kPlayListServerId; adId.ad = (*hPlayList)->playListId; if (pPlayListState=FindAdState(adId,&playListStateIdx)) if (hKeepAdList) (*hKeepAdList)[playListStateIdx]=true; } XMLDecIndent(); AccuAddTagLine(&a,PlayListKeywords[kPlayListTkn],true); } if (hKeepAdList) { PrunePlayState(hKeepAdList); ZapHandle(hKeepAdList); } } // screen size rScreen = GetScreenBounds(); if (AdWin) { SetPort_(GetWindowPort(GetMyWindowWindowPtr(AdWin))); // RectDepth needs this depth = RectDepth(&AdWin->contR); } else depth = (*(*GetMainDevice())->gdPMap)->pixelSize; AccuAddXMLWithAttr(&a,PlayListKeywords[kScreenTkn],ComposeString(s,"\pheight=\"%d\" width=\"%d\" depth=\"%d\"",RectHi(rScreen),RectWi(rScreen),depth),true); // distributorID if (text = GetDistributorID()) { AccuAddXMLObjectHandle(&a,PlayListKeywords[kDistIDTkn],text); ZapHandle(text); } // faceTime for every day of week faceTimeLeft = 0; *s = 0; for(i=0;i<7;i++) { long time = (*gPlayState)->faceTimeWeek[i]; // if today's value, take larger of last week and this if (i==(*gPlayState)->today.dayOfWeek-1 && (*gPlayState)->faceTimeToday > (*gPlayState)->faceTimeWeek[i]) time = (*gPlayState)->faceTimeToday; if (i) PCatC(s,','); NumToString(time/60,s2); PCat(s,s2); } AccuAddXMLObject(&a,PlayListKeywords[kFaceTimeTkn],s); // faceTimeLeft faceTimeLeft = TotalFaceTimeLeft(); NumToString(faceTimeLeft/60,s); AccuAddXMLObject(&a,PlayListKeywords[kFaceTimeLeftTkn],s); // faceTimeUsedToday NumToString((*gPlayState)->adFaceTimeToday/60,s); AccuAddXMLObject(&a,PlayListKeywords[kFaceTimeUsedTodayTkn],s); // pastry (cookie) if(hPastry = GetResourceFromFile(kStateResType,kPastryResId,SettingsRefN)) { if (!PastryIsStale(hPastry)) AccuAddXMLWithAttrPtr(&a,PlayListKeywords[kPastryTkn],LDRef(hPastry),GetHandleSize(hPastry),true); UL(hPastry); ReleaseResource(hPastry); } // user profile if (text = GetProfileData()) { AccuAddXMLObjectHandle(&a,PlayListKeywords[kUserProfileTkn],text); } // playListVersion NumToString(kPlayListVersion,s); AccuAddXMLObject(&a,PlayListKeywords[kPlayListVersionTkn],s); #ifdef LOGINFO // Send date/time and Id info for server log { Str255 sDate,sTime; long secs = LocalDateTime(); IUTimeString(secs,false,sTime); IUDateString(secs,shortDate,sDate); AccuAddXMLObject(&a,PlayListKeywords[kDateAndTime],ComposeString(s,"\p%p, %p",sDate,sTime)); AccuAddXMLObject(&a,PlayListKeywords[kUserId],GetPref(s,PREF_POP)); } #endif XMLDecIndent(); AccuAddTagLine(&a,PlayListKeywords[kClientUpdateTkn],true); AccuTrim(&a); // Do not dispose of data in handle. DownloadURL will dispose of it httpStuff.hRequestData = a.data; NewTempSpec (Root.vRef,Root.dirId,nil,&spec); gPLDownloadInProgress = true; *s = 0; #ifdef DEBUG GetRString(s,DEBUG_PLAYLIST_URL); #endif if (!*s) PCopy(s,PlayListURL); s[*s+1] = 0; // Include checksum SetupForDisplay(LDRef(a.data),GetHandleSize(a.data),key,false); Bytes2Hex(key,sizeof(key),httpStuff.sCheckSum+1); *httpStuff.sCheckSum = 32; err = DownloadURL(s+1,&spec,nil,FinishedPlayListDownload,&reference,&httpStuff); if (err) { gPlayListDownloadFail = true; gPLDownloadInProgress = false; } // Now is a good time to tidy up the cache CleanupAdCache(false); // Remember when we did this gPlayListFetchGMT = GMTDateTime(); } /************************************************************************ * GetAdSpec - get filespec for ad ************************************************************************/ static OSErr GetAdSpec(FSSpecPtr spec, AdId adId) { short adIdx; Str255 sURL; AdInfoPtr pAd; PlayListHandle hPlayList; if (pAd = FindAd(adId,&adIdx,&hPlayList)) { GetPlayListURLString(hPlayList,pAd->srcURL,sURL); GetCacheSpec(sURL,spec,true); return noErr; } else return fnfErr; } /************************************************************************ * MakePastry - save a pastry (cookie) ************************************************************************/ static void SavePastry(Ptr pPastry,long len) { short oldRes = CurResFile(); if (SettingsRefN) UseResFile(SettingsRefN); ZapResourceLo(kStateResType,kPastryResId,true); AddPResource(pPastry,len,kStateResType,kPastryResId,nil); UseResFile(oldRes); } /************************************************************************ * BuildPlayList - parse play list ************************************************************************/ static OSErr BuildPlayList(UHandle text) { #define kMaxTokenStack 64 PlayList list; TokenInfo tokenInfo; short tokenType; Str255 sToken; short tokenStack[kMaxTokenStack]; short stackIdx = -1; Accumulator accuKeywordList; OSErr err; AdInfo entryInfo; short i; PlayStatePtr pState; DateTimeRec nextPlaylist; Accumulator aStrings,aState,aPlayList; PlayListHandle hPlayList; long level; // keep track of nag level // Initialize tokenizer tokenInfo.pText = LDRef(text); tokenInfo.size = GetHandleSize(text); tokenInfo.offset = 0; // Add our keywords to keyword list if (err = AccuInit(&accuKeywordList)) return err; AccuAddPtr(&accuKeywordList,&i,sizeof(short)); // Make room for count for(i=0;*PlayListKeywords[i];i++) { err=AccuAddPtr(&accuKeywordList,PlayListKeywords[i],*PlayListKeywords[i]+1); if (err) return err; } *(short *)*accuKeywordList.data = i; tokenInfo.aKeywords = &accuKeywordList; ZapHandle(gDownloadErrString); AccuInitWithHandle(&aState,(Handle)gPlayState); // Parse loop while ((tokenType = GetNextToken(&tokenInfo)) != kTokenDone) { TokenToString(&tokenInfo, sToken); //ComposeLogS(LOG_PLIST,nil,"\pxml token: %d %p",tokenType,sToken); if (tokenType == kContent) { // Get element content long zone; if (stackIdx >= 1 && tokenStack[stackIdx-1] == kPlayListTkn) { // Defining play list elements switch (tokenStack[stackIdx]) { case kPlayListIdTkn: list.playListId = Hash(sToken); AddStringToHandle(&list.playListIDString,&aStrings,sToken); break; case kMixAlgorithmTkn: list.mixType = StringSame(PlayListKeywords[kBlockTkn],sToken) ? kBlock : kMix; break; case kBlockScheduleAlgorithmTkn: list.blockScheduleType = StringSame(PlayListKeywords[kRandomTkn],sToken) ? kRandom : kLinear; break; case kClickBaseTkn: TokenToURL(&list.clickBase,&aStrings,&tokenInfo); break; } } else if (stackIdx >= 1 && tokenStack[stackIdx-1] == kClientInfoTkn) { // Defining client info elements switch (tokenStack[stackIdx]) { case kReqIntervalTkn: (*gPlayState)->reqInterval = PStrToNum(sToken); if ((*gPlayState)->reqInterval > kMaxReqInterval) (*gPlayState)->reqInterval = kMaxReqInterval; break; case kHistLengthTkn: (*gPlayState)->histLength = PStrToNum(sToken); break; case kScheduleTkn: (*gPlayState)->schedType = StringSame(PlayListKeywords[kRandomTkn],sToken) ? kRandom : kLinear; break; case kWidthTkn: (*gPlayState)->adWidth = PStrToNum(sToken); break; case kHeightTkn: (*gPlayState)->adHeight = PStrToNum(sToken); break; case kFaceTimeQuotaTkn: (*gPlayState)->faceTimeQuota = PStrToNum(sToken)*60; break; // Convert to seconds case kRerunIntervalTkn: (*gPlayState)->rerunInterval = PStrToNum(sToken)*24*60*60; break; // Convert days to seconds case kNagTkn: HandleNag(level,sToken); break; case kClientModeTkn: HandleClientMode(sToken); break; case kUserProfileTkn: HandleUserProfile(sToken); break; } } else if (stackIdx >= 2 && tokenStack[stackIdx-2] == kPlayListTkn && tokenStack[stackIdx-1] == kEntryTkn) { // Defining ad entry elements switch (tokenStack[stackIdx]) { case kShowForTkn: entryInfo.showFor = PStrToNum(sToken); break; case kDayMaxTkn: entryInfo.dayMax = PStrToNum(sToken); break; case kShowForMax: entryInfo.showForMax = PStrToNum(sToken); list.totalShowMax += entryInfo.showForMax; break; case kSrcTkn: TokenToURL(&entryInfo.srcURL,&aStrings,&tokenInfo); break; case kBlackBeforeTkn: entryInfo.blackBefore = PStrToNum(sToken); break; case kBlackAfterTkn: entryInfo.blackAfter = PStrToNum(sToken); break; case kStartDateTkn: entryInfo.startDate = BeautifyDate(sToken,&zone); entryInfo.startDate+=zone; break; case kEndDateTkn: entryInfo.endDate = BeautifyDate(sToken,&zone); entryInfo.endDate+=zone; break; case kAdIdTkn: ParseAdId(sToken,&entryInfo.adId); break; case kTitleTkn: AddStringToHandle(&entryInfo.title,&aStrings,sToken); break; case kClickTkn: TokenToURL(&entryInfo.clickURL,&aStrings,&tokenInfo); break; } } else if (stackIdx >= 1 && tokenStack[stackIdx-1] == kFaultTkn) { // Defining fault elements switch (tokenStack[stackIdx]) { case kFaultCodeTkn: (*gPlayState)->errCode = gDownloadErrCode = PStrToNum(sToken); break; case kFaultStringTkn: gDownloadErrString = NewString(sToken); Log(LOG_PLIST,sToken); break; } } } else { // Process a tag short tokenIdx,attrIdx; long value; tokenIdx = GetTokenIdx(&accuKeywordList,sToken)-1; switch (tokenType) { case kElementTag: case kEmptyElementTag: switch (tokenIdx) { case kEntryTkn: // New ad entry Zero(entryInfo); while(attrIdx = GetNumAttribute(&tokenInfo,&value)) { switch (attrIdx) { case kIsButtonTkn: if (value) entryInfo.type=kIsButton; break; case kIsSponsorTkn: if (value) entryInfo.type=kIsSponsor; break; case kIsRunoutTkn: if (value) entryInfo.type=kIsRunout; break; } } break; case kSrcTkn: if (GetAttribute(&tokenInfo,sToken) == kCheckSum && *sToken==32) // Checksum for src Hex2Bytes(sToken+1,32,entryInfo.checksum); break; case kPlayListTkn: // New playlist Zero(list); gPlayList = NuHandleClear(sizeof(PlayList)); AccuInitWithHandle(&aPlayList,(Handle)gPlayList); if (!gPlayList) return MemError(); // Accumulator for strings AccuInit(&aStrings); AccuAddChar(&aStrings,0); // We don't want any strings with offset zero // Set up some default values list.version = kPlayListVers; break; case kFlushTkn: if (tokenStack[stackIdx]==kClientInfoTkn) Flush(&tokenInfo); break; case kPastryTkn: if (tokenStack[stackIdx]==kClientInfoTkn) SavePastry(tokenInfo.pText+tokenInfo.attrStart,tokenInfo.attrLen); break; case kNagTkn: level = 0; if (GetAttribute(&tokenInfo,sToken) == kLevelTkn) StringToNum(sToken,&level); break; case kResetTkn: // The server has determined that we are insane // Reset data and request a new playlist ResetAdState(); AccuInitWithHandle(&aState,(Handle)gPlayState); // gPlayState has been resized break; case kClientInfoTkn: // Set up default client info in case some values // aren't included (*gPlayState)->reqInterval = kReqInterval; (*gPlayState)->histLength = kHistLength; (*gPlayState)->schedType = kSchedType; (*gPlayState)->adWidth = kAdWidth; (*gPlayState)->adHeight = kAdHeight; (*gPlayState)->faceTimeQuota = kFaceTimeQuota * 60; (*gPlayState)->rerunInterval = kRerunInterval * 24*3600; break; } if (tokenType==kElementTag) { // New element. Push token on stack // Don't put empty tags on stack. There is no end-tag if (stackIdx < kMaxTokenStack-1) tokenStack[++stackIdx] = tokenIdx; } break; case kEndTag: // End of element. Pop off stack. Actually search whole stack // in case document is not well-formed. while (stackIdx >= 0) { // if (tokenStack[stackIdx--] == tokenIdx) break; } switch (tokenIdx) { case kEntryTkn: // Save ad entry if (!AccuAddPtr(&aPlayList,&entryInfo,sizeof(entryInfo))) { list.numAds++; DeleteAd(entryInfo.adId); } break; case kPlayListTkn: // Save playlist if (hPlayList=FindPlayList(list.playListId)) { // We're replacing this playlist. Delete it. REMOVE_AND_DESTROY(hPlayList); } AccuTrim(&aPlayList); list.strOffset = GetHandleSize_(gPlayList); **gPlayList = list; // Save final playlist info // Add strings to end of playlist AccuTrim(&aStrings); if (HandAndHand(aStrings.data,(Handle)gPlayList)) return MemError(); AccuZap(aStrings); // Save playlist resource AddMyResource_(gPlayList,kPlayListResType,UniqueID(kPlayListResType),""); UpdateResFile(SettingsRefN); LL_Queue(gPlayListQueue,gPlayList,(PlayListHandle)); AddStates(gPlayList,&aState,true); break; } break; } } } SecondsToDate(LocalDateTime()+((*gPlayState)->reqInterval?(*gPlayState)->reqInterval:kReqInverval)*60L*60L,&nextPlaylist); pState = *gPlayState; pState->getNextPlaylist = nextPlaylist; pState->dirty = true; gAdFetchCount = 0; pState->adsNotFetched = 0; gDownloadErrCode = pState->errCode; AccuTrim(&aState); // Don't dispose of this accumulator! SavePlayStatus(); SetCurrentPlayList(); UL(text); #ifdef DEBUG SetupAdDebugMenu(); #endif return noErr; } /************************************************************************ * ResetAdState - throw away all ad data and request a new playlist ************************************************************************/ static void ResetAdState(void) { // Delete playlists while (gPlayListQueue) { PlayListHandle hPlayList; hPlayList = gPlayListQueue; REMOVE_AND_DESTROY(hPlayList); } // Reinialize ad state SetupAdState(true); // Get rid of all cached ads CleanupAdCache(true); // Nothing to display right now ShowBlackTimeImage(); gBlackTime = -1; // Negative value means display black until an ad shows up RollGlobalFacetime(); RequestPlaylist(); #ifdef DEBUG { MenuHandle mh; if (mh = GetMHandle(AD_SELECT_HIER_MENU)); TrashMenu(mh,gAdsMenuAdsStart); } #endif } /************************************************************************ * DeleteAd - delete this ad from every playlist ************************************************************************/ static void DeleteAd(AdId adId) { short idx; PlayListHandle hPlayList; while (FindAd(adId,&idx,&hPlayList)) { PlayListPtr pPlayList = *hPlayList; pPlayList->numAds--; if (pPlayList->numAds) { long playListLen = GetHandleSize((Handle)hPlayList); long moveBytes = (long)*hPlayList + playListLen - (long)&pPlayList->ads[idx+1]; pPlayList->strOffset -= sizeof(AdInfo); BlockMove(&pPlayList->ads[idx+1],&pPlayList->ads[idx],moveBytes); SetHandleSize((Handle)hPlayList,playListLen-sizeof(AdInfo)); ChangedResource((Handle)hPlayList); } else { // Nothing left in this playlist. Let's get rid of it. REMOVE_AND_DESTROY(hPlayList); } } #ifdef DEBUG SetupAdDebugMenu(); #endif } /************************************************************************ * AddStates - make sure all ads are in playstate ************************************************************************/ static void AddStates(PlayListHandle hPlayList, AccuPtr aState, Boolean init) { AdId adId; AdState state; short i; AdStatePtr pAdState; for(i=-1;i<(*hPlayList)->numAds;i++) { if (i==-1) { // Start with playlist state adId.server = kPlayListServerId; adId.ad = (*hPlayList)->playListId; } else adId = (*hPlayList)->ads[i].adId; Zero(state); state.adId = adId; state.lastDisplayTime = LocalDateTime(); if (pAdState = FindAdState(adId,nil)) { // Reinitialize ad state if (init) { *pAdState = state; (*gPlayState)->dirty = true; } } else { // Not already there. Add it. if (!AccuAddPtr(aState,&state,sizeof(AdState))) { (*gPlayState)->numAds++; (*gPlayState)->dirty = true; } } } } /************************************************************************ * DisposePlaylists - dispose of play lists ************************************************************************/ static void DisposePlaylists(void) { PlayListHandle hPlayList,nextPlaylist; for(hPlayList=gPlayListQueue;hPlayList;hPlayList=nextPlaylist) { nextPlaylist = (*hPlayList)->next; ReleaseResource((Handle)hPlayList); } gPlayListQueue = nil; gPlayList = nil; } /************************************************************************ * PeteIsInAdWindow - dispose of play list ************************************************************************/ Boolean PeteIsInAdWindow(PETEHandle pte) { WindowPtr pteWinWP = GetMyWindowWindowPtr ((*PeteExtra(pte))->win); return pte && GetWindowKind(pteWinWP)==AD_WIN; } /************************************************************************ * ValidAdImage - return error if image is in ad window and has invalid checksum ************************************************************************/ OSErr ValidAdImage(PETEHandle pte,Handle hData) { OSErr err = noErr; if (PeteIsInAdWindow(pte)) if (err = CheckAd((*gPlayState)->curAd,hData,nil)) // Bad ad. Display the next ad gDoNextAd = true; return err; } /************************************************************************ * CheckAd - return error if ad has invalid checksum ************************************************************************/ static OSErr CheckAd(AdId adId,Handle hData,FSSpec *spec) { OSErr err = noErr; AdInfoPtr pAd; PlayListHandle hPlayList; if (pAd = FindAd(adId,nil,&hPlayList)) { short i; Boolean hasChecksum = false; AdInfo ad = *pAd; for(i=0;idirty = true; AddAdToLinkHistory(pAd->adId, nil, nil, spec); } } } /************************************************************************ * FindPlayList - find this playlist ************************************************************************/ static PlayListHandle FindPlayList(long playListId) { PlayListHandle hPlayList; for(hPlayList=gPlayListQueue;hPlayList;hPlayList=(*hPlayList)->next) if ((*hPlayList)->playListId == playListId) return hPlayList; return nil; } /************************************************************************ * SetCurrentPlayList - set gPlayList to current playlist ************************************************************************/ static void SetCurrentPlayList(void) { PlayListHandle hPlayList; if (gPlayState && (hPlayList=FindPlayList((*gPlayState)->curPlayList))) gPlayList = hPlayList; else gPlayList = gPlayListQueue; } /************************************************************************ * FindAd - find this ad in a playlist ************************************************************************/ static AdInfoPtr FindAd(AdId adId,short *idx,PlayListHandle *playList) { short adIdx; PlayListHandle hPlayList; AdInfoPtr pAd; for(hPlayList=gPlayListQueue;hPlayList;hPlayList=(*hPlayList)->next) for (adIdx=0,pAd=(*hPlayList)->ads;adIdx<(*hPlayList)->numAds;adIdx++,pAd++) if (SameAd(&adId,&pAd->adId)) { if (idx) *idx = adIdx; if (playList) *playList = hPlayList; return pAd; } return nil; } /************************************************************************ * FindAdType - find a valid ad of this type in a playlist ************************************************************************/ static AdInfoPtr FindAdType(AdType adType,short *idx,PlayListHandle *playList) { return FindAdTypeLo(adType,0,gPlayListQueue,idx,playList); } /************************************************************************ * FindAdTypeLo - find a valie ad of this type in a playlist ************************************************************************/ static AdInfoPtr FindAdTypeLo(AdType adType,short startIdx,PlayListHandle startPlayList,short *idx,PlayListHandle *playList) { AdInfoPtr pAd; PlayListHandle hPlayList; short adIdx; AdStatePtr pAdState; uLong curTime = LocalDateTime(); for(hPlayList=startPlayList;hPlayList;hPlayList=(*hPlayList)->next) { for (adIdx=startIdx,pAd=(*hPlayList)->ads+startIdx;adIdx<(*hPlayList)->numAds;adIdx++,pAd++) if (adType == pAd->type) if (pAdState = FindAdState(pAd->adId,nil)) if (!pAdState->deleted) if (ValidAd(hPlayList,adIdx,curTime,vaCheckDayMax+vaCheckShowForMax)) { if (idx) *idx = adIdx; if (playList) *playList = hPlayList; return pAd; } startIdx = 0; } return nil; } /************************************************************************ * FindCurrentAdIdx - find current ad in playlist ************************************************************************/ static AdInfoPtr FindCurrentAdIdx(short *idx) { return FindAd((*gPlayState)->curAd,idx,&gPlayList); } /************************************************************************ * FindCurrentAd - find current ad in playlist ************************************************************************/ static AdInfoPtr FindCurrentAd(void) { return FindCurrentAdIdx(nil); } /************************************************************************ * FindAdState - find this ad in the playstate ************************************************************************/ static AdStatePtr FindAdState(AdId adId,short *idx) { short adIdx; AdStatePtr pAd; for (adIdx=0,pAd=(*gPlayState)->stateAds;adIdx<(*gPlayState)->numAds;adIdx++,pAd++) if (SameAd(&adId,&pAd->adId)) { if (idx) *idx = adIdx; return pAd; } // Not found return nil; } /************************************************************************ * FindCurrentAdState - find current ad in playState ************************************************************************/ static AdStatePtr FindCurrentAdState(void) { return FindAdState((*gPlayState)->curAd,nil); } /************************************************************************ * FindPlayListState - find current playlist in playState ************************************************************************/ static AdStatePtr FindPlayListState(PlayListHandle hPlayList) { AdId adId; AdStatePtr pAdState=nil; if (hPlayList) { adId.server = kPlayListServerId; adId.ad = (*hPlayList)->playListId; if (!(pAdState=FindAdState(adId,nil))) { Accumulator aState; AccuInitWithHandle(&aState,(Handle)gPlayState); AddStates(hPlayList, &aState, false); AccuTrim(&aState); // Don't dispose of this accumulator! pAdState=FindAdState(adId,nil); } } return pAdState; } /************************************************************************ * CleanupAdCache - purge old ads ************************************************************************/ static void CleanupAdCache(Boolean withPrejuidice) { uLong cutoffDate = LocalDateTime()-kPurgeAdDays*60*60*24; Str32 name; FSSpec spec; CInfoPBRec hfi; // (jp) 9-10-99 If we can't find the Ad folder, don't delete anything // You mean, if you CAN find the ad folder, don't delete anything. Yikes! SD 12/5/99 if (!SubFolderSpec(AD_FOLDER_NAME,&spec)) { hfi.hFileInfo.ioNamePtr = name; hfi.hFileInfo.ioFDirIndex = 0; while (!DirIterate(spec.vRefNum,spec.parID,&hfi)) { if (withPrejuidice || hfi.hFileInfo.ioFlMdDat < cutoffDate) { // Old enough, delete it PCopy(spec.name,name); if (!FSpDelete(&spec)) // (jp) 9-10-99 Only decrement the index if there was no err hfi.hFileInfo.ioFDirIndex--; } } } } /************************************************************************ * TotalFaceTimeLeft - how much face time do we have lying around? ************************************************************************/ long TotalFaceTimeLeft(void) { PlayListHandle hPlayList; long faceTimeLeft = 0; for(hPlayList=gPlayListQueue;hPlayList;hPlayList=(*hPlayList)->next) { AdStatePtr pPlayListState; if ((*hPlayList)->totalShowMax && (pPlayListState=FindPlayListState(hPlayList))) { long thisFaceTimeLeft; thisFaceTimeLeft = (*hPlayList)->totalShowMax - pPlayListState->shownTotal; if (thisFaceTimeLeft < 0) thisFaceTimeLeft = 0; faceTimeLeft += thisFaceTimeLeft; } } return faceTimeLeft; } #ifdef LOGADS /************************************************************************ * DoLogAds - log ad scheduling ************************************************************************/ static void DoLogAds(StringPtr sLog) { FSSpec spec; Str31 scratch; // Don't log ads unless Log window is open SimpleMakeFSSpec(Root.vRef,Root.dirId,GetRString(scratch,LOG_NAME),&spec); if (FindText(&spec)) Log(LOG_MORE,sLog); } #endif #ifdef DEBUG /************************************************************************ * ToggleAdWindow - open/close ad window ************************************************************************/ void ToggleAdWindow(Boolean resetPlayState) { if (AdWin) CloseAdWindow(); else { // Reset play state before opening ad window short oldRes = CurResFile(); if (resetPlayState) { if (SettingsRefN) UseResFile(SettingsRefN); Zap1Resource(kStateResType,kStateResId); UseResFile(oldRes); } gPlayState = nil; OpenAdWindow(); RequestPlaylist(); } } /************************************************************************ * DebugAdMenu - do debug ad menu item ************************************************************************/ void DebugAdMenu(short item,short modifiers) { FSSpec spec; switch (item) { newPlayList: case dbNewPlaylist: if (modifiers&optionKey) ResetAdState(); RequestPlaylist(); break; case dbReloadPlaylist: SubFolderSpec(AD_FOLDER_NAME,&spec); PCopy(spec.name,sPlayListFileName); NewPlaylist(&spec,nil); break; case dbDumpPlaylists: DumpPlaylists((modifiers&optionKey)!=0); break; case dbDefaultPLServer: SetPref(DEBUG_PLAYLIST_URL,""); goto newPlayList; default: if (gPlayList && item >= gAdsMenuAdsStart) { PlayListHandle hPlayList; short adIdx; adIdx = item-gAdsMenuAdsStart; for(hPlayList=gPlayListQueue;hPlayList;hPlayList=(*hPlayList)->next) { if (adIdx < (*hPlayList)->numAds) break; adIdx -= (*hPlayList)->numAds+1; } if (hPlayList && adIdx < (*hPlayList)->numAds) { gPlayList = hPlayList; NewAd(adIdx); } gBlackTime = 0; // Start ad right away } else if (item > dbDefaultPLServer) { Str127 server; MyGetItem(GetMHandle(AD_SELECT_HIER_MENU),item,server); SetPref(DEBUG_PLAYLIST_URL,server); goto newPlayList; } break; } } /************************************************************************ * SetupAdDebugMenu - setup ad debug menu ************************************************************************/ static void SetupAdDebugMenu() { MenuHandle mh; Str255 s; if (RunType!=Production && (mh=GetMHandle(AD_SELECT_HIER_MENU))) { short i; PlayListHandle hPlayList; if (!gAdsMenuAdsStart) { gAdsMenuAdsStart = dbDefaultPLServer; MyAppendMenu(mh,PlayListURL); gAdsMenuAdsStart++; for (i=1;*GetRString(s,PLAYLIST_SERVER_STRN+i);i++) {MyAppendMenu(mh,s); gAdsMenuAdsStart++;} AppendMenu(mh,"\p-"); gAdsMenuAdsStart++; } TrashMenu(mh,gAdsMenuAdsStart); for(hPlayList=gPlayListQueue;hPlayList;hPlayList=(*hPlayList)->next) { for(i=0;i<(*hPlayList)->numAds;i++) { GetPlayListString(hPlayList,(*hPlayList)->ads[i].title,s); if (!*s) PCopy(s,"\pUntitled"); if ((*hPlayList)->ads[i].type) { PCatC(s,' '); PCatC(s,'('); PCatC(s,'0'+(*hPlayList)->ads[i].type); PCatC(s,')'); } MyAppendMenu(mh,s); } if ((*hPlayList)->next) AppendMenu(mh,"\p-"); } } } /************************************************************************ * CheckCurrentAd - check the current ad in ad debug menu ************************************************************************/ void CheckCurrentAd(void) { MenuHandle mh; Str127 server; short mItem; if (mh=GetMHandle(AD_SELECT_HIER_MENU)) { uLong curTime = LocalDateTime(); CheckNone(mh); // Check playlist server GetRString(server,DEBUG_PLAYLIST_URL); if (!*server) PCopy(server,PlayListURL); SetItemMark(mh,FindItemByName(mh,server),checkMark); // check ad if (gPlayList) { short adIdx; PlayListHandle hPlayList; MCEntry menuColor,*pMenuColor; FindCurrentAdIdx(&adIdx); for(hPlayList=gPlayListQueue;hPlayList;hPlayList=(*hPlayList)->next) { if (hPlayList==gPlayList) break; adIdx += (*hPlayList)->numAds+1; } SetItemMark(mh,adIdx+gAdsMenuAdsStart,checkMark); // Mark inactive ones mItem = gAdsMenuAdsStart; Zero(menuColor); menuColor.mctID = AD_SELECT_HIER_MENU; SetRGBGrey(&menuColor.mctRGB2,0x8000); if (pMenuColor = GetMCEntry(AD_SELECT_HIER_MENU,0)) menuColor.mctRGB4 = pMenuColor->mctRGB4; for(hPlayList=gPlayListQueue;hPlayList;hPlayList=(*hPlayList)->next) { for(adIdx=0;adIdx<(*hPlayList)->numAds;adIdx++,mItem++) { if (ValidAd(hPlayList,adIdx,curTime,vaCheckDayMax+vaCheckShowForMax)) { DeleteMCEntries(AD_SELECT_HIER_MENU,mItem); } else { // Make item appear to be disabled menuColor.mctItem = mItem; SetMCEntries(1,&menuColor); } if (!ValidAd(hPlayList,adIdx,curTime,vaCheckShowForMax)) SetItemStyle(mh,mItem,italic); } mItem++; } } } } Accumulator DumpAcc; short DumpIndent; /************************************************************************ * DumpPlaylists - dump playlist info to a text window ************************************************************************/ void DumpPlaylists(Boolean fullDisplay) { // For every block that's not in our list, dump it to log MyWindowPtr win; PlayListHandle hPlayList; Str255 s,s2; DateTimeRec date; AccuInit(&DumpAcc); DumpIndent = 0; AccuDump(ComposeString(s,"\pTotal number of ads: %d",(*gPlayState)->numAds)); AccuDump(ComposeString(s,"\pHistory length: %d",(*gPlayState)->histLength)); date = (*gPlayState)->getNextPlaylist; AccuDump(ComposeString(s,"\pRequest next playlist: %d/%d/%d %d:%d",date.month,date.day,date.year,date.hour,date.minute)); AccuDump(ComposeString(s,"\pFetch error code: %d",(*gPlayState)->errCode)); AccuDump(ComposeString(s,"\pRerun mode: %p",(*gPlayState)->outOfAds?"\pyes":"\pno")); RollGlobalFacetime();// update faceTimeToday AccuDump(ComposeString(s,"\pFacetime today: %d",(*gPlayState)->faceTimeToday)); AccuDump(ComposeString(s,"\pAd facetime today: %d",(*gPlayState)->adFaceTimeToday)); AccuDump(ComposeString(s,"\pFacetime quota: %d",(*gPlayState)->faceTimeQuota)); AccuDump(ComposeString(s,"\pFaceTime week: %d,%d,%d,%d,%d,%d,%d",(*gPlayState)->faceTimeWeek[0], (*gPlayState)->faceTimeWeek[1],(*gPlayState)->faceTimeWeek[2],(*gPlayState)->faceTimeWeek[3], (*gPlayState)->faceTimeWeek[4],(*gPlayState)->faceTimeWeek[5],(*gPlayState)->faceTimeWeek[6])); AccuDump(ComposeString(s,"\pRerun interval: %d",(*gPlayState)->rerunInterval/(24*60*60))); AccuDump(ComposeString(s,"\pAd Failure States: Failed--%B Succeeded--%B Checked Mail--%B Checked Mail Today--%B", NoAdsRec.adsAreFailing, NoAdsRec.adsAreSucceeding, NoAdsRec.checkedMail, NoAdsRec.checkedMailToday)); AccuDump(ComposeString(s,"\pAd Failure Counters: DeadbeatCounter %d ConsecutiveDays %d", NoAdsRec.deadbeatCounter, NoAdsRec.consecutiveDays)); for(hPlayList=gPlayListQueue;hPlayList;hPlayList=(*hPlayList)->next) { short adIdx; AccuAddChar(&DumpAcc,'\r'); GetPlayListString(hPlayList,(*hPlayList)->playListIDString,s2); AccuDump(ComposeString(s,"\pPlaylist \"%p\", Ad count: %d, Show max total: %d",s2,(*hPlayList)->numAds,(*hPlayList)->totalShowMax)); DumpIndent++; for(adIdx=0;adIdx<(*hPlayList)->numAds;adIdx++) { AdInfo ad = (*hPlayList)->ads[adIdx]; AdStatePtr pAdState; GetPlayListString(hPlayList,ad.title,s2); AccuDump(ComposeString(s,"\p\"%p\" Id: %d.%d",s2,ad.adId.server,ad.adId.ad)); DumpIndent++; if (ad.type) AccuDump(ComposeString(s,"\pType: %p",ad.type==1?"\pToolbar button":ad.type==2?"\pSponsor":"\pRunout")); AccuDump(ComposeString(s,"\pshowFor: %d, dayMax: %d, showForMax: %d",ad.showFor,ad.dayMax,ad.showForMax)); if (ad.blackBefore || ad.blackAfter) AccuDump(ComposeString(s,"\pblackBefore: %d, blackAfter: %d",ad.blackBefore,ad.blackAfter)); AccuDumpTime("\pStart Date",ad.startDate); AccuDumpTime("\pEnd Date",ad.endDate); if (pAdState = FindAdState(ad.adId,nil)) { AdState state = *pAdState; AccuDump(ComposeString(s,"\pShown today: %d, shown total: %d",state.shownToday,state.shownTotal)); AccuDumpTime("\pLast display",state.lastDisplayTime); } if (fullDisplay) { GetPlayListURLString(hPlayList,ad.srcURL,s2); AccuDump(ComposeString(s,"\pSource URL: %p",s2)); GetPlayListURLString(hPlayList,ad.clickURL,s2); AccuDump(ComposeString(s,"\pClick URL: %p",s2)); } DumpIndent--; } DumpIndent--; } if (!(win = FindTextLo(nil,"\pPlaylist Info"))) win = OpenText(nil,nil,nil,nil,true,"\pPlaylist Info",true,false); if (!win) return; win->close = DumpClose; (*PeteExtra(win->pte))->spelled = sprNeverSpell; AccuTrim(&DumpAcc); PeteSetText(win->pte,DumpAcc.data); AccuZap(DumpAcc); } /************************************************************************ * DumpClose - don't prompt for save on window close ************************************************************************/ static Boolean DumpClose(MyWindowPtr win) { return(true); } /************************************************************************ * AccuDump - write a line to dump accumulator ************************************************************************/ static void AccuDump(StringPtr s) { short i; for(i=0;i<3*DumpIndent;i++) AccuAddChar(&DumpAcc,' '); // Do indentation AccuAddStr(&DumpAcc,s); AccuAddChar(&DumpAcc,'\r'); } /************************************************************************ * AccuDumpTime - write date/time to dump accumulator ************************************************************************/ static void AccuDumpTime(StringPtr sName, uLong seconds) { Str255 s; DateTimeRec date; if (seconds) { SecondsToDate(seconds, &date); AccuDump(ComposeString(s,"\p%p: %d/%d/%d %d:%d",sName,date.month,date.day,date.year,date.hour,date.minute)); } } #endif /************************************************************************ * PreFetchAds - load all ads into cache ************************************************************************/ static void PreFetchAds(void) { short i; Str255 sURL; FSSpec spec; PlayListHandle hPlayList; if (gAdDownloadList) return; // We're still fetching ads (*gPlayState)->adsNotFetched = 0; for(hPlayList=gPlayListQueue;hPlayList;hPlayList=(*hPlayList)->next) { for(i=0;i<(*hPlayList)->numAds;i++) { GetPlayListURLString(hPlayList,(*hPlayList)->ads[i].srcURL,sURL); GetCacheSpec(sURL,&spec,true); if (FSpExists(&spec)) { // Don't have ad in cache. Go get it. Handle url; if (url = GetPlayListURL(hPlayList,(*hPlayList)->ads[i].srcURL)) { if (!QueueAdDownload(url,(*hPlayList)->ads[i].adId,(*hPlayList)->ads[i].type)) ZapHandle(url); // Error, didn't queue, is duplicate } } } } } /************************************************************************ * QueueAdDownload - put ad in download queue if not duplicate ************************************************************************/ static Boolean QueueAdDownload(Handle url, AdId adId, AdType adType) { DownloadListHandle hDownload; for(hDownload=gAdDownloadList;hDownload;hDownload=(*hDownload)->next) { // See if we already have this ad in the queue if (SameAd(&adId,&(*hDownload)->adId) || !strcmp(*url,*(*hDownload)->url)) return false; // Already in queue } if (hDownload = NuHandleClear(sizeof(DownloadList))) { (*hDownload)->url = url; (*hDownload)->adId = adId; (*hDownload)->adType = adType; LL_Push(gAdDownloadList,hDownload); return true; } return false; } /************************************************************************ * CheckDownloadList - see if we can download any ads ************************************************************************/ static void CheckDownloadList(void) { while (gAdDownloadList && gDownloadCount < kMaxDownloads) { Handle url = (*gAdDownloadList)->url; long reference; Str255 sURL; FSSpec spec; DownloadListHandle hDownload = gAdDownloadList; gAdDownloadList = (*gAdDownloadList)->next; // Take ad off queue *sURL=strlen(LDRef(url)); BlockMoveData(*url,sURL+1,*sURL); GetCacheSpec(sURL,&spec,true); FindTemporaryFolder(Root.vRef,Root.dirId,&spec.parID,&spec.vRefNum); if (TestWriteToAdFolder() || DownloadURL(*url,&spec,(long)hDownload,FinishedAdDownload,&reference,nil)) { ZapHandle(hDownload); ZapHandle(url); (*gPlayState)->adsNotFetched++; } else { gAdFetchCount++; gDownloadCount++; } } } /************************************************************************ * TestWriteToAdFolder - is it ok to write to the ad folder? ************************************************************************/ OSErr TestWriteToAdFolder(void) { FSSpec spec; OSErr err, deleteErr; Boolean can; SubFolderSpec(AD_FOLDER_NAME,&spec); PCopy(spec.name,"\ptest.gif"); if (err=FSpCreate(&spec,'GIF ','GIF ',smSystemScript)) if (err!=dupFNErr) return err; err = CanWrite(&spec,&can); deleteErr = FSpDelete(&spec); if (deleteErr && !err) err = deleteErr; return err; } /************************************************************************ * AdCheckingMail - checking mail, user is online, need to fetch ads and/or playlist? ************************************************************************/ void AdCheckingMail(void) { RollGlobalFacetime(); // record facetime now NoAdsRec.checkedMailToday = true; SavePlayStatus(); if (AdWin && (gPlayListDownloadFail || !gPlayListQueue)) // Haven't been able to get a playlist yet RequestPlaylist(); else if (gPlayState && !gAdFetchCount && (*gPlayState)->adsNotFetched) // There are ads that we haven't been able to fetch yet PreFetchAds(); else if (gPlayState && TotalFaceTimeLeft()<(*gPlayState)->faceTimeQuota/2 // Running low on ads && GMTDateTime()-gPlayListFetchGMT > kMinRetryInterval) // But don't check too often { RequestPlaylist(); } } /************************************************************************ * RollGlobalFacetime - record facetime so far today and start recording again ************************************************************************/ void RollGlobalFacetime(void) { long time; if (!gGlobalFaceMeasure) if (gGlobalFaceMeasure = NewFaceMeasure()) FaceMeasureBegin(gGlobalFaceMeasure); if (gGlobalFaceMeasure) { if (FaceMeasureReport(gGlobalFaceMeasure,&time,nil,nil,nil)) return; if (gPlayState && time) { (*gPlayState)->faceTimeToday += time; (*gPlayState)->dirty = true; } #ifdef DEBUG ComposeLogS(LOG_PLIST,nil,"\pFaceTimeRoll %ds %dm",time,gPlayState ? (*gPlayState)->faceTimeToday/60:-1); #endif FaceMeasureReset(gGlobalFaceMeasure); } } /************************************************************************ * PastryIsStale - is the pastry stale? ************************************************************************/ OSErr PastryIsStale(Handle pastry) { UPtr spot, end; short count=0; // non-ascii chars? if (AnyFunny(pastry,0)) return 1; // Unbalanced quotes? end = *pastry + GetHandleSize(pastry); for (spot=*pastry;spotserver); PToken(sToken,sNum,&spot,"."); StringToNum(sNum,&pAdId->ad); } /************************************************************************ * ShowBlackTimeImage - display image between ads ************************************************************************/ static void ShowBlackTimeImage(void) { WindowPtr AdWinWP = GetMyWindowWindowPtr (AdWin); PicHandle pic = GetResource_('PICT',GRAY_LOGO_PICT); Rect rPic,rWin; if (pic && AdWinWP) { HNoPurge_(pic); SetPort_(GetWindowPort(AdWinWP)); rPic = (*pic)->picFrame; rWin = AdWin->contR; CenterRectIn(&rPic,&rWin); if (rPic.top > 0 || rPic.left > 0) { // Pic is too small for window. Erase first. EraseRect(&rWin); } DrawPicture(pic,&rPic); HPurge((Handle)pic); } } /************************************************************************ * AddStringToHandle - add a string to a handle ************************************************************************/ static OSErr AddStringToHandle(StrOffset *offset,AccuPtr a,StringPtr s) { *offset = a->offset; return AccuAddPtr(a,s,(*s)+1); } /************************************************************************ * GetPlayListString - get a string from a playlist ************************************************************************/ static StringPtr GetPlayListString(PlayListHandle hPlayList,StrOffset offset,StringPtr s) { if (hPlayList && offset) PCopy(s,(*(Handle)hPlayList)+(*hPlayList)->strOffset + offset); else *s = 0; return s; } /************************************************************************ * GetPlayListURL - get a URL string from a playlist * Returns a handle the caller must dispose of ************************************************************************/ static Handle GetPlayListURL(PlayListHandle hPlayList,StrOffset offset) { Handle hURL = nil; char *pURL; if (hPlayList && offset) { LDRef(hPlayList); pURL = (*(Handle)hPlayList)+(*hPlayList)->strOffset + offset; hURL = NuDHTempOK(pURL, strlen(pURL)+1); UL(hPlayList); } return hURL; } /************************************************************************ * GetPlayListURLString - get a URL as a p-string from a playlist ************************************************************************/ static StringPtr GetPlayListURLString(PlayListHandle hPlayList,StrOffset offset,StringPtr s) { if (hPlayList && offset) { Ptr dest; uLong len; dest = *(Handle)hPlayList; dest += (*hPlayList)->strOffset + offset; len=strlen(dest); *s=min(len,255); BlockMoveData(dest,s+1,*s); } else *s = 0; return s; } /************************************************************************ * Flush - flush a playlist or ad ************************************************************************/ static void Flush(TokenInfo *pInfo) { enum { kIsPlayList=1,kIsEntry,kIsProfileID } what; Str255 s,sId; short tokenIdx; what = 0; *sId = 0; while(tokenIdx = GetAttribute(pInfo,s)) { switch (tokenIdx) { case kEntityTkn: if (StringSame(PlayListKeywords[kPlayListTkn],s)) what = kIsPlayList; else if (StringSame(PlayListKeywords[kEntryTkn],s)) what = kIsEntry; else if (StringSame(PlayListKeywords[kUserProfileTkn],s)) what = kIsProfileID; break; case kIdTkn: PCopy(sId,s); break; } if (what && (*sId || what==kIsProfileID)) { if (what==kIsPlayList) { // Flush playlist PlayListHandle hPlayList; if (hPlayList = FindPlayList(Hash(sId))) REMOVE_AND_DESTROY(hPlayList); } else if (what==kIsProfileID) { SetProfileID(""); } else { // Flush entry AdId adId; ParseAdId(sId,&adId); DeleteAd(adId); } // Set up to get some more. I think this shouldn't happen what = 0; *sId = 0; } } } /************************************************************************ * SameAdId - are the ad id's the same? ************************************************************************/ Boolean SameAd(AdId *pAd1,AdId *pAd2) { return pAd1->server == pAd2->server && pAd1->ad == pAd2->ad; } /************************************************************************ * AdWinNeedsNetwork - do we currently need the network connection? ************************************************************************/ Boolean AdWinNeedsNetwork(void) { return (gAdDownloadList || gPLDownloadInProgress || gDownloadCount); } /************************************************************************ * PrunePlayState - remove old ads from play state handle ************************************************************************/ static void PrunePlayState(Handle hKeepAdList) { short i,delCount = 0; for(i=0;i<(*gPlayState)->numAds;i++) { if ((*hKeepAdList)[i]) { // Keep this one if (delCount) // Need to move it (*gPlayState)->stateAds[i-delCount] = (*gPlayState)->stateAds[i]; } else // Delete this one delCount++; } if (delCount) { // We deleted some. Adjust play state handle (*gPlayState)->numAds -= delCount; SetHandleSize((Handle)gPlayState,GetHandleSize((Handle)gPlayState)-sizeof(AdState)*delCount); (*gPlayState)->dirty = true; } } /************************************************************************ * MakeAdIconFromFile - create an ad icon from a file * * 32x32 and 16x16 icons are stored in the file in this format: +----------------------+-----------+ | | | | |16x16 image| | 32x32 image | | | |-----------+ | | | | +----------------------+ ************************************************************************/ static void MakeAdIconFromFile(FSSpecPtr spec, Handle iconSuite) { OSErr err; GraphicsImportComponent importer; GWorldPtr gWorld = nil; Rect r; Handle hIcon; // figure out which importer can open the ad picture file ... if (!GetGraphicsImporterForFile(spec, &importer)) { // Create an offscreen GWorld to do the deed in ... SetRect(&r,0,0,48,32); err = NewGWorld(&gWorld, 8, &r, nil, nil, useTempMem); // Try temp memory first if (err) err = NewGWorld(&gWorld, 8, &r, nil, nil, nil); // Failed, use application heap if (!err) { // Draw into offscreen world RGBColor color,*transparentColor=nil; if (GetPNGTransColor(importer,spec,&color)) transparentColor = &color; // Use PNG transparent color when setting up mask PushGWorld(); SetGWorld(gWorld,nil); if (!GraphicsImportSetGWorld(importer, gWorld, nil)) if (!GraphicsImportDraw(importer)) { SetRect(&r,32,0,48,16); if (hIcon = MakeIcon(gWorld,&r,8,16)) AddIconToSuite(hIcon,iconSuite,kSmall8BitData); if (hIcon = MakeICN_pound(gWorld,&r,16,transparentColor)) AddIconToSuite(hIcon,iconSuite,kSmall1BitMask); SetRect(&r,0,0,32,32); if (hIcon = MakeIcon(gWorld,&r,8,32)) AddIconToSuite(hIcon,iconSuite,kLarge8BitData); if (hIcon = MakeICN_pound(gWorld,&r,32,transparentColor)) AddIconToSuite(hIcon,iconSuite,kLarge1BitMask); } PopGWorld(); DisposeGWorld(gWorld); } CloseComponent(importer); } } /************************************************************************ * SetupSponsorAd - setup sponsor ad ************************************************************************/ void SetupSponsorAd(void) { AdInfoPtr pAd; PlayListHandle hPlayList; Str255 sURL; FSSpec spec; if (!IsPayMode() // Free and adware only && HaveQuickTime(0x0300)) // In free mode, QuickTime is not required thus may not be installed { if (pAd = FindAdType(kIsSponsor,nil,&hPlayList)) { if (!gSponsorAdPic || !SameAd(&gSponsorAdId,&pAd->adId)) { WindowPtr winWP; MyWindowPtr win; GraphicsImportComponent importer; StrOffset srcURL = pAd->srcURL; gSponsorAdId = pAd->adId; GetPlayListURLString(hPlayList,srcURL,sURL); GetCacheSpec(sURL,&spec,true); ZapSponsorAd(); if (!FSpExists(&spec)) { short oldResFile = CurResFile(); // Graphic importing changes curresfile if (!CheckAd(gSponsorAdId,nil,&spec)) if (!GetGraphicsImporterForFile(&spec,&importer)) { SetupPNGTransparency(importer,&spec); GraphicsImportGetAsPicture(importer,&gSponsorAdPic); CloseComponent(importer); } if (!gSponsorAdPic) Zero(gSponsorAdId); UseResFile(oldResFile); } else { // Need to download this sponsor ad Handle url; if (url = GetPlayListURL(hPlayList,srcURL)) { if (!QueueAdDownload(url,gSponsorAdId,kIsSponsor)) ZapHandle(url); // Error, didn't queue, is duplicate } } // Set up windows to display sponsor ad // Telling it to resize will do for (winWP = FrontWindow (); winWP; winWP = GetNextWindow (winWP)) if (IsKnownWindowMyWindow(winWP)) if (win = GetWindowMyWindowPtr (winWP)) if (win->showsSponsorAd) MyWindowDidResize(win,nil); } } else // Make sure we don't have a sponsor ad anymore ZapSponsorAd(); } } /************************************************************************ * ClickSponsorAd - process click if it's in sponsor ad ************************************************************************/ Boolean ClickSponsorAd(MyWindowPtr win,EventRecord *event,Point pt) { if (win->showsSponsorAd && win->sponsorAdExists && PtInRect(pt,&win->sponsorAdRect)) { AdUserClick(gSponsorAdId); return true; } return false; } /************************************************************************ * DrawSponsorAd - draw sponsor ad if there is one ************************************************************************/ void DrawSponsorAd(MyWindowPtr win) { if (gSponsorAdPic && win->showsSponsorAd && win->sponsorAdExists) DrawPicture(gSponsorAdPic,&win->sponsorAdRect); } /************************************************************************ * ZapSponsorAd - get rid of sponsor ad if there is one ************************************************************************/ static void ZapSponsorAd(void) { if (gSponsorAdPic) { // Get rid of old ad KillPicture(gSponsorAdPic); gSponsorAdPic = nil; } } /************************************************************************ * ResetAdSchedule - start from top on ad scheduling ************************************************************************/ static void ResetAdSchedule(void) { (*gPlayState)->curAd.server = 0; (*gPlayState)->curAd.ad = 0; (*gPlayState)->dirty = true; } /************************************************************************ * SetupWinSponsorInfo - make sure this window had sponsor ad info if it needs it ************************************************************************/ void SetupWinSponsorInfo(MyWindowPtr win) { if (win->showsSponsorAd) { if (gSponsorAdPic) { Rect rPortRect; CGrafPtr winPort = GetMyWindowCGrafPtr (win); Rect rSponsor = (*gSponsorAdPic)->picFrame; Rect rWin = *GetPortBounds(winPort,&rPortRect); OffsetRect(&rSponsor,rWin.right-RectWi(rSponsor)-kSponsorBorderMargin-GROW_SIZE-rSponsor.left, rWin.bottom-RectHi(rSponsor)-kSponsorBorderMargin-rSponsor.top); win->sponsorAdRect = rSponsor; win->sponsorAdExists = true; } else win->sponsorAdExists = false; } } /************************************************************************ * MakeAdIcon - create a toolbar ad icon from URL ************************************************************************/ static void MakeAdIcon(PlayListHandle hPlayList,StrOffset srcURL,AdId adId,Handle iconSuite,Boolean downloadOK) { FSSpec spec; Str255 s; GetPlayListURLString(hPlayList,srcURL,s); GetCacheSpec(s,&spec,true); if (!FSpExists(&spec) && !CheckAd(adId,nil,&spec)) MakeAdIconFromFile(&spec,iconSuite); else if (downloadOK) { // Need to download icon Handle url; // Put into c-string handle if (url = NuDHTempOK(s+1,(*s)+1)) { (*url)[*s]=0; // null-terminate if (!QueueAdDownload(url,adId,kIsButton)) ZapHandle(url); // Error, didn't queue, is duplicate } } } /************************************************************************ * SetTBAdIcon - set this toolbar ad icon that just finished downloading ************************************************************************/ static void SetTBAdIcon(AdId adId) { AdInfoPtr pAd; Handle iconSuite = nil; short i; TBAdInfo *pAdInfo; PlayListHandle hPlayList; if (!NewIconSuite(&iconSuite)) { if (pAd = FindAd(adId,nil,&hPlayList)) { MakeAdIcon(hPlayList,pAd->srcURL,adId,iconSuite,false); TBUpdateAdButtonIcon(adId,iconSuite); } } if (gTBAds) { for (pAdInfo=*gTBAds,i=HandleCount(gTBAds);i--;pAdInfo++) if (SameAd(&pAdInfo->adId,&adId)) { pAdInfo->iconSuite = iconSuite; break; } } } /************************************************************************ * AdGetTBIcon - get icons for toolbar ad ************************************************************************/ Handle AdGetTBIcon(AdId adId) { AdInfoPtr pAd; PlayListHandle hPlayList; Handle iconSuite = nil; LoadPlaylists(); // Ad window may not be initialized yet if (pAd = FindAd(adId,nil,&hPlayList)) { StrOffset srcURL = pAd->srcURL; if (!NewIconSuite(&iconSuite)) MakeAdIcon(hPlayList,srcURL,adId,iconSuite,true); } return iconSuite; } /************************************************************************ * SetupToolbarAds - set up ad toolbar ads ************************************************************************/ static void SetupToolbarAds(void) { Accumulator a; PlayListHandle hPlayList; AdInfoPtr pAd; short adIdx; TBAdInfo adInfo; Str255 s; short i; TBAdInfo *pAdInfo; if (gTBAds) { // Add to existing list AccuInitWithHandle(&a,(Handle)gTBAds); for (pAdInfo=*gTBAds,i=a.offset/sizeof(**gTBAds);i--;pAdInfo++) pAdInfo->deleted = true; } else { // New list of toolbar ads if (AccuInit(&a)) return; gTBAds = (TBAdHandle)a.data; } if (IsAdwareMode()) // toolbar ads in Light? I don't think so. (!IsPayMode()) { for(adIdx=0,hPlayList=gPlayListQueue;pAd=FindAdTypeLo(kIsButton,adIdx,hPlayList,&adIdx,&hPlayList);adIdx++) { Boolean found; // Do we already have this ad? found = false; for (pAdInfo=*gTBAds,i=a.offset/sizeof(**gTBAds);i--;pAdInfo++) { if (SameAd(&pAdInfo->adId,&pAd->adId)) { found = true; pAdInfo->deleted = false; break; } } if (!found) { // Add new one to list StrOffset srcURL = pAd->srcURL; adInfo.adId = pAd->adId; GetPlayListString(hPlayList,pAd->title,s); PStrCopy(adInfo.title,s,sizeof(adInfo.title)); if (!NewIconSuite(&adInfo.iconSuite)) MakeAdIcon(hPlayList,srcURL,adInfo.adId,adInfo.iconSuite,true); adInfo.deleted = false; AccuAddPtr(&a,&adInfo,sizeof(adInfo)); } } } else { a.offset = 0; // Remove ads if in pay mode } AccuTrim(&a); TBAddAdButtons(gTBAds); if (!GetHandleSize_(gTBAds)) ZapHandle(gTBAds); } /************************************************************************ * AdDeleteButton - a toolbar ad has been deleted by user ************************************************************************/ void AdDeleteButton(AdId adId) { AdStatePtr pAdState; if (pAdState = FindAdState(adId,nil)) { pAdState->deleted = true; (*gPlayState)->dirty = true; } } /************************************************************************ * AdValidate - validate ad/playlist with MD5 * This function is intentionally a bit messy to try and confuse any * hacker looking for our secret. It also has a non-descriptive function * name for the same reason. ************************************************************************/ OSErr SetupForDisplay(Ptr data,long dataLen,unsigned char checksum[kCheckSumSize],Boolean check) { MD5_CTX md5; static Secret0 = 0x15028A81; long *in = (long*)&md5.in[0]; OSErr result = noErr; // char secret[] = "\pJello be dim."; MD5Init(&md5); // MD5Update(&md5,secret+1,*secret); // The following does the same as the previous line but // without revealing the secret string in[3] = Secret3^0x4b99fe13; md5.i[0] = 104; in[1] = Secret1^0x0747db4b; md5.i[1] = 0; md5.buf[0] = 0x67452301; md5.buf[1] = 0xEFCDAB89; in[0] = Secret0^0x5f67e6ed; md5.buf[2] = 0x98BADCFE; md5.buf[3] = 0x10325476; in[2] = Secret2^0x8361e9bf; MD5Update(&md5,data,dataLen); MD5Final(&md5); if (check) { // Check this checksum if (memcmp(md5.digest,checksum,kCheckSumSize)) result = (*gPlayState)->errCode = badCksmErr; // Validation failure! } else { // Return this checksum BMD(md5.digest,checksum,kCheckSumSize); } return result; } /************************************************************************ * InitAdPte - setup pte for ad window ************************************************************************/ static void InitAdPte(void) { CGrafPtr AdWinPort = GetMyWindowCGrafPtr (AdWin); PETEDocInitInfo initInfo; RGBColor bkColor; DefaultPII(AdWin,false,0,&initInfo); initInfo.inParaInfo.startMargin = 0; initInfo.inParaInfo.endMargin = WindowWi(GetMyWindowWindowPtr(AdWin))+1; PeteCreate(AdWin,&ADpte,0,&initInfo); CleanPII(&initInfo); PeteLock(ADpte,0,0x7fffffff,peModLock|peSelectLock); PETESetDefaultBGColor(PETE,ADpte,GetPortBackColor(AdWinPort,&bkColor)); PeteDidResize(ADpte,&AdWin->contR); } /************************************************************************ * GetAdFolderSpec - get spec for ad folder, creating if necessary ************************************************************************/ OSErr GetAdFolderSpec(FSSpecPtr spec) { Str255 s; CInfoPBRec hfi; long junk; OSErr err=noErr; FSMakeFSSpec(Root.vRef,Root.dirId,GetRString(s,AD_FOLDER_NAME),spec); IsAlias(spec,spec); if (FSpGetHFileInfo(spec,&hfi)) err = FSpDirCreate(spec,smSystemScript,&junk); else { // Make sure user hasn't locked folder or replaced it with a file if (hfi.hFileInfo.ioFlAttrib&0x01) // It was locked. Unlock it. err = FSpRstFLock(spec); if (!(hfi.hFileInfo.ioFlAttrib&0x10)) { // Not a folder. Delete it and create folder. if (!(err = FSpDelete(spec))) err = FSpDirCreate(spec,smSystemScript,&junk); } } return err; } /************************************************************************ * GetLanguageCode - get language code in "xx-xx" format ************************************************************************/ UPtr GetLanguageCode(UPtr sResult) { Str255 s; short index; index = (short)GetScriptManagerVariable(smRegionCode)+1; if (index >= 100) index++; // 1-99 and 101-109 GetRString(s,LanguageIdStrn+index); PStrCopy(sResult,s,6); return sResult; } /************************************************************************ * HandleNag - tell Eudora the user needs to be nagged ************************************************************************/ OSErr HandleNag(long level,PStr text) { if (level==0) { ZapResource(NAG_REQUEST_TYPE,PLNAG_LEVEL0_DLOG); AddPResource(text+1,*text,NAG_REQUEST_TYPE,PLNAG_LEVEL0_DLOG,""); } if (level==1) { ZapResource(NAG_REQUEST_TYPE,PLNAG_LEVEL1_DLOG); AddPResource(text+1,*text,NAG_REQUEST_TYPE,PLNAG_LEVEL1_DLOG,""); } if (level==2) { ZapResource(NAG_REQUEST_TYPE,NAG_INTRO_DLOG); AddPResource(text+1,*text,NAG_REQUEST_TYPE,NAG_INTRO_DLOG,""); } PlaylistNagCount++; return noErr; } /************************************************************************ * HandleUserProfile - store a profile given to us by the playlist server ************************************************************************/ OSErr HandleUserProfile(PStr text) { return SetProfileID(text); } /************************************************************************ * HandleClientMode - switch modes based on playlist server orders; * or, actually, tell the main loop to do so ************************************************************************/ OSErr HandleClientMode(PStr text) { long newMode=-1; StringToNum(text,&newMode); if (newMode>=0) NewClientModePlusOne = newMode+1; return noErr; } /************************************************************************ * ForecPlaylistRequest - force a playlist request ************************************************************************/ void ForcePlaylistRequest(void) { RequestPlaylist(); } /************************************************************************ * GetSponsorAdInfo - get some information about the sponsor ad ************************************************************************/ void GetSponsorAdTitle(UPtr sTitle) { AdInfoPtr pAd; PlayListHandle hPlayList; if (pAd = FindAdType(kIsSponsor,nil,&hPlayList)) GetPlayListString(hPlayList,pAd->title,sTitle); else *sTitle = 0; } #endif