eudora-mac/adwin.c

1 line
122 KiB
C
Executable File

/* 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()<nextIdle) return; // Once a second is sufficient
nextIdle = TickCount() + 60;
if (!gPlayState) SetupAdState(false);
NewDayCheck(false); // Have we gone to a new day?
if (gActiveConnections>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;i<pState->numAds;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<?xml version=\"1.0\"?>");
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;i<kCheckSumSize;i++)
if (ad.checksum[i])
{
hasChecksum = true;
break;
}
if (hasChecksum) // If no checksum specified, ad is OK
{
Boolean disposeData = false;
if (!hData && spec)
{
Snarf(spec,&hData,0);
disposeData = true;
}
if (hData)
{
err = SetupForDisplay(LDRef(hData),GetHandleSize(hData),ad.checksum,true);
if (err)
{
// Bad ad.
if (IsChecksumFailAgain(adId))
// We've redownloaded and failed again. Get rid of this ad.
DeleteAd(adId);
else
{
// Bad ad. Delete it and download again
FSSpec delSpec;
Handle url;
if (spec) delSpec = *spec;
else GetAdSpec(&delSpec,adId);
FSpDelete(&delSpec);
// Download ad again.
if (url = GetPlayListURL(hPlayList,ad.srcURL))
{
if (!QueueAdDownload(url,ad.adId,ad.type))
ZapHandle(url); // Error, didn't queue, is duplicate
}
}
}
if (disposeData) ZapHandle(hData);
}
}
}
return err;
}
/************************************************************************
* IsChecksumFailAgain - has this ad failed a checksum previously?
************************************************************************/
static Boolean IsChecksumFailAgain(AdId adId)
{
static AdId **hFailList;
Boolean found = false;
if (!hFailList) hFailList = NuHandle(0);
else
{
short i,n = HandleCount(hFailList);
for(i=0;i<n;i++)
if (SameAd(&adId,&(*hFailList)[i]))
return true;
}
PtrAndHand(&adId,(Handle)hFailList,sizeof(adId));
return false;
}
/************************************************************************
* IsAdInPlaylist - is this ad in the current playlist?
************************************************************************/
Boolean IsAdInPlaylist(AdId adId)
{
return FindAd(adId,nil,nil) != nil;
}
/************************************************************************
* AdWinGotImage - a graphic was just inserted--do we need to report it?
************************************************************************/
void AdWinGotImage(PETEHandle pte,FSSpecPtr spec)
{
if (PeteIsInAdWindow(pte))
{
AdStatePtr pAd;
if (pAd = FindCurrentAdState())
{
// Give link window graphic filespec
(*gPlayState)->dirty = 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;spot<end;spot++)
if (*spot=='"') count++;
if (count%2) return 2;
return 0;
}
/************************************************************************
* AdCheckingMail - checking mail, user is online, need to fetch ads and/or playlist?
************************************************************************/
static void ParseAdId(StringPtr sToken, AdId *pAdId)
{
UPtr spot;
Str32 sNum;
spot = sToken+1;
PToken(sToken,sNum,&spot,".");
StringToNum(sNum,&pAdId->server);
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