/* counter.c -- * "Access Counter" * Written by J. Patrick Van Metre for * World Wide Web: Beyond the Basics * CS 6204 at Virginia Polytechnic Institute and State University * * This CGI application returns the location of a GIF image which * can be displayed to represent a single digit in the count of * accesses to a WWW page. */ #include #include #include #include #define STRING_LENGTH 1024 #define SEPARATOR '&' #define DATABASE "/home/vanmetre/cgi-db/counter/counter.db" #define PICTURE_LOC "http://csgrad.cs.vt.edu/~vanmetre/numbers/" #define LOCK_FILE "/home/vanmetre/cgi-db/counter/LOCK" /* GetParam -- Accepts a query string in Query an searches it * for a term named Name. * Returns the value assigned to Name in Dest. */ void GetParam(char *Dest, const char *Name, const char *Query) { int Pos=0, copy, QueryLen; QueryLen = strlen(Query); while (Pos < QueryLen) { /* Iterate through */ if (!strncmp(Query+Pos, Name, strlen(Name))) { /* query looking */ Pos += strlen(Name)+1; /* for Name; if */ copy = 0; /* found, copy to */ while ((Query[Pos] != SEPARATOR) && (Pos < QueryLen)) /* Dest */ Dest[copy++] = Query[Pos++]; Dest[copy] = 0; return; } /* if */ else { /* If not found, move along to the next */ Pos += strlen(Name)+1; /* name-value pair in query */ while ((Query[Pos] != SEPARATOR) && (Pos < QueryLen)) Pos++; Pos++; } /* else */ } /* while */ } /* GetParam */ void main() { char Query[STRING_LENGTH]; char ReqDigit[STRING_LENGTH]; char Location[STRING_LENGTH]; char Temp[STRING_LENGTH]; char TempLocation[STRING_LENGTH]; char NewDBName[STRING_LENGTH]; char Command[STRING_LENGTH]; int NumAccess, Value, Work, CurrDigit, Digit; int TempNumAccess; int Found = 0; FILE *OldDB, *NewDB; int lock_fd; /* Remember the query string and the URL of the page being counted */ strcpy(Query, getenv("QUERY_STRING")); strcpy(Location, getenv("HTTP_REFERER")); /* Retrieve the value for "digit=" from the query string */ GetParam(ReqDigit, "digit", Query); Digit = atoi(ReqDigit); /* Get a lock on the counter database file */ lock_fd = open(LOCK_FILE, O_RDONLY | O_CREAT, 256+128+32+16); if (lock_fd < 0) { printf("

Error opening lock file

\n"); exit(1); } flock(lock_fd, LOCK_EX); /* Open the database file for reading */ OldDB = fopen(DATABASE, "r"); if (OldDB == NULL) { printf("Content-type: text/html\n\n"); printf("Couldn't open db for reading\n"); exit(1); } /* We only update a location's counter when the ones digit is accessed */ if (Digit == 0) { /* And when we update, we write a new database file, then rename it */ sprintf(NewDBName, "%s.new", DATABASE); NewDB = fopen(NewDBName, "w"); if (NewDB == NULL) { printf("Content-type: text/html\n\n"); printf("Couldn't open db for writing\n"); exit(1); } } /* Read through the database file looking for the accessed URL */ while (!feof(OldDB)) { fgets(Temp, STRING_LENGTH, OldDB); /* Read one line at a time */ if (!feof(OldDB)) { /* Make sure we haven't read past EOF */ sscanf(Temp, "%d %s", &TempNumAccess, TempLocation); if (!strcmp(TempLocation, Location)) { /* If the accessed URL is */ NumAccess = TempNumAccess; /* already in the database */ Found = 1; /* increment the number of */ NumAccess++; /* accesses */ if (Digit == 0) fprintf(NewDB, "%d %s\n", NumAccess, Location); } else { if (Digit == 0) fputs(Temp, NewDB); } } } fclose(OldDB); if (Digit == 0) { fclose(NewDB); sprintf(Command, "cp -f %s %s", NewDBName, DATABASE); system(Command); } /* If the URL isn't in the database, then just stick it on the end */ if (!Found) { OldDB = fopen(DATABASE, "a"); if (OldDB == NULL) { printf("Content-type: text/html\n\n"); exit(1); } NumAccess = 1; fprintf(OldDB, "%d %s\n", 1, Location); fclose(OldDB); } /* Unlock the database file */ flock(lock_fd, LOCK_UN); close(lock_fd); Value = 0; CurrDigit = 0; Work = NumAccess; /* Isolate the digit we want */ while (Work != 0) { if (CurrDigit == Digit) Value = Work % 10; CurrDigit++; Work /= 10; } /* Refer the accessed URL to the location of the digit image */ /* by putting it in the header output */ printf("Location: %s%d.gif\n\n", PICTURE_LOC, Value); }