/************************************************************************* PROGRAM: DisplayLinks FILE: dsplnks.cpp (main program) PURPOSE: Main program for DspLnks DSAPI sample: URL syntax rules: /path/db.nsf/view/unid/$Links.html /path/db.nsf/view/unid/$Links.json Examples: /Sales/Quarterly.nsf/0/506096553C0C925105256595006097F1/$Links.html /Sales/Quarterly.nsf/0/506096553C0C925105256595006097F1/$Links.json JSON results in format: {"DisplayLinks": { "links": [ {"replica": "885257B620001FEF", "viewUNID": "34027A0E7C5B464885257B620001FEF2", "UNID": "29177C537F8C06D885257B5F005B4A7A", "foundText": "Document from different database" } ], "Count": 1 }} Steps: 1) Identify by /$Links (match with ?GetJSON or use html) 2) Parse out db, unid and ext, fail if any not correct or licensed 4) Open db or fail 5) Open unid or fail 6) Render as doc and return is pieces as needed *************************************************************************/ #ifdef __cplusplus extern "C" { #endif #define USE_NOTESINIT_NOTESTERM /* Input and output include files */ #include #include #include /* Notes SDK include files */ #include "global.h" #include "nsfdb.h" #include "osmem.h" #include "lookup.h" #include "dsapi.h" #include "addin.h" #include "osenv.h" #include "intl.h" #if !defined(ND64) #define DHANDLE HANDLE #endif #define MAX_BUF_LEN 512 #define USER_DOMAIN_SEPARATOR "@" USHORT debug_level; /*--- * local procedure prototypes */ /* Notes SDK unix shared library entrypoint */ STATUS FAR PASCAL MainEntryPoint (void); /* Routines with syntax dictated by the DSAPI interface */ unsigned int HandleParsedRequest(FilterContext* context, FilterParsedRequest* reqData); BOOL ConvertTIMEDATEToSMTPDate(TIMEDATE *timedate_, char *ret_buffer); BOOL IsAllHex(char *str, USHORT len); char *stristr (char *buffer, char *substr); char *doclink_symb_ = "R0lGODlhDAAOALMAAAAAAP///7q6w7m5wrW1vf7+/u/v7+Hh4dLS0sDAwLu7u7KysqKiooCAgP//" "/wAAACH5BAEAAA4ALAAAAAAMAA4AAARC0MkGmrwXhKbqwtoWBAslAV4zjsBJNuraOuEqu/Z4zPV6" "BLzcDxH0BRC7BK2gOx6IiwWqsXAiAVFJyfPEKjFb1CcCADs="; char *viewlink_symb_ = "R0lGODlhDAAOAIQPAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD/" "/////////////////////////////////////////////////////////////////////yH+Gk9u" "ZSBSaW5nIHRvIGJyaW5nIHRoZW0gYWxsACH5BAEKAA8ALAAAAAAMAA4AAAUx4COOZGmOQKqu6nMc" "yxPPKWLfeCrvs5wuL1jw8ND1jr8gMFgE8J6L5Gv5arKup6w2BAA7"; char *dblink_symb_ = "R0lGODlhDAAOAIQPAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD/" "/////////////////////////////////////////////////////////////////////yH+FU9u" "ZSBSaW5nIHRvIGZpbmQgdGhlbQAh+QQBCgAPACwAAAAADAAOAAAFMOAjjmRpng9BoKpzrKR6uC6c" "0nMOEwyOr7xe7pdiCH87I02GGAVlL1uRED2ppChSCAA7"; #define DL_DISPLAY_AS_HTML 0 #define DL_DISPLAY_AS_JSON 1 /*--- * local procedures follow */ STATUS FAR PASCAL MainEntryPoint (void) { /* * Description: Provide a main entry point for the Notes API to * initialize inside of this shared library. We need * this entry point because we want to be able to lookup * user information in the name and address book via the * Notes SDK API. * * Input: nothing * Output: nothing * Return: NOERROR */ return TRUE; } /*--- * filter initialization */ unsigned int FilterInit(FilterInitData* filterInitData) { /* * Description: Filter initialization is performed when the filter * shared library is dynamically loaded. * * Input: filterInitData dsapi specification controls the format * of data * Output: filterInitData several fields are filled in * * Return: kFilterHandledEvent */ /*Required*/ filterInitData->appFilterVersion = kInterfaceVersion; /* Modify the following code to set the flags you want */ //filterInitData->eventFlags = kFilterParsedRequest|kFilterRawWrite; filterInitData->eventFlags = kFilterParsedRequest; /* Set a short description for your filter */ strcpy(filterInitData->filterDesc, "DisplayLinks"); AddInLogMessageText("DisplayLinks: Demo initialized", NOERROR); /* Output sent to stdout and stderr is displayed on the * server console, but is not written to the server log file. */ return kFilterHandledEvent; } /*--- * filter termination */ unsigned int TerminateFilter(unsigned int reserved) { /* * Description: Filter termination is performed when the filter * shared library is unloaded. * * Input: reserved currently unused (dsapi spec controls the * format of data) * Output: none * * Return: kFilterHandledEvent */ /* insert any global cleanup code here... */ debug_level = OSGetEnvironmentInt("DisplayLinksDebug"); return kFilterHandledEvent; } /*--- * filter notification handling */ unsigned int HttpFilterProc(FilterContext* context, unsigned int eventType, void* eventData) { /* * Description: This routine is called for all dsapi filter events. * * Input: reserved currently unused (dsapi spec controls the * format of data) * Output: none * * Return: kFilterNotHandled for all events that we don't customize, * otherwise allow our filter routine to provide a return * value. */ /* Include only those events we want to handle */ switch (eventType) { case kFilterParsedRequest: return HandleParsedRequest(context, (FilterParsedRequest *)eventData); default: break; } return kFilterNotHandled; } /*--- * handle parsed request */ unsigned int HandleParsedRequest(FilterContext* context, FilterParsedRequest* reqData) { /* * Description: This routine is called on a dsapi kFilterAuthUser * event. * * Input: context dsapi specification controls the format of data * * reqData request information, most importantly the URL * * Return: kFilterNotHandled if we do not understand the input data, or it does not apply in some way * kFilterHandledEvent if we have handled the request */ unsigned int errid; unsigned int reserved; char dbname[256]; char servername[132]; char webservername[132]; char filepath[MAXPATH]; char unid_buffer[33]; USHORT dblen; USHORT i; USHORT k; FilterRequest request; int ret; char *str; char *str2; char *str3; char *poststr; STATUS sError; DBHANDLE hDB; UNID unid; NOTEHANDLE hNote; NOTEHANDLE hNote2; USHORT action; WORD count = 0; char modstr[256]; unsigned int len; TIMEDATE timedate; char buffer[NSF_INFO_SIZE+32]; char *retstr; LIST List; NOTELINK *notelink_; BLOCKID bidLinksItem; DWORD dwLinksValueLen; BLOCKID bidLinksValue; BYTE *pLinksValue; WORD wLinksType; DBREPLICAINFO repinfo; char retbuffer[4096]; /* Rule #1 - Eliminate requests not with as little processing as possible. */ /* Simply return kFilterNotHandled so Domino can take over processing, */ /* making sure not to allocate any resources, or free ones you have. */ if (!reqData || reqData->requestMethod != kRequestGET) return (kFilterNotHandled); memset((char *)&request, '\0', sizeof(FilterRequest)); poststr = NULL; str = NULL; if (!context->GetRequest(context, &request, &errid) || request.URL == NULL || (poststr = stristr(request.URL, "/$Links")) == NULL || (_strnicmp(poststr, "/$Links.html", 11) != 0 && _strnicmp(poststr, "/$Links.json", 12) != 0)) { if (poststr != NULL && debug_level > 0) AddInLogMessageText("DisplayLinks: Did not handle '%s' due to reasons", NOERROR, request.URL); return(kFilterNotHandled); } /* Rule #2 - Check all lengths before copying to avoid buffer overruns, and use copies for parsing/changing. */ strncpy(modstr, request.URL, 255); modstr[255] = '\0'; if ((poststr = stristr(modstr, "/$Links")) == NULL) return(kFilterNotHandled); action = DL_DISPLAY_AS_HTML; if (_strnicmp(poststr, "/$Links.json", 12) == 0) action = DL_DISPLAY_AS_JSON; /* Rule #3 - Check for characters/sequences that might trigger code. */ if (strchr(modstr, '\'') != NULL || strchr(modstr, '"') != NULL || strchr(modstr, '@') != NULL) { if (debug_level > 0) AddInLogMessageText("DisplayLinks: Did not handle due to invalid characters", NOERROR); return(kFilterNotHandled); } /* Rule #4 - Have strict rules about formatting of URL to minimize risk. */ if ((str = stristr(modstr, ".nsf")) != NULL) { dblen = (USHORT) (str-modstr+3); if (dblen == 0 || dblen > 250) { if (debug_level > 0) AddInLogMessageText("DisplayLinks: Did not handle '%s' due to db length", NOERROR, modstr); return (kFilterNotHandled); } strncpy(dbname, modstr+1, dblen); dbname[dblen] = '\0'; str += 4; if (*str != '/' || (str2 = strchr(str+1, '/')) == NULL || _strnicmp(str2, "/$Links", 7) == 0 || (str3 = strchr(str2+1, '/')) == NULL || _strnicmp(str3, "/$Links", 7) != 0 || (str3-str2) != 33 || !IsAllHex(str2+1, 32)) { if (debug_level > 0) AddInLogMessageText("DisplayLinks: Did not handle '%s' due to missing view or doc UNID", NOERROR, modstr); return(kFilterNotHandled); } else { str++; str2++; *str3 = '\0'; } } else { if (debug_level > 0) AddInLogMessageText("DisplayLinks: Did not handle '%s' due to missing db", NOERROR, modstr); return(kFilterNotHandled); } /* Rule #5 - Keep track of resources allocated, handles opened and such, as you will need to clean up on failure */ sError = NSFDbOpen(dbname, &hDB); if (sError != NOERROR) { if (debug_level > 0) AddInLogMessageText("DisplayLinks: Did not handle '%s' due to invalid db", sError, modstr); return (kFilterNotHandled); } strncpy(unid_buffer, str2, 32); unid_buffer[32] = '\0'; /* Note part second, reading backwards in buffer */ unid.Note.Innards[0] = (DWORD) strtoul(unid_buffer+24, NULL, 16); unid_buffer[24] = '\0'; unid.Note.Innards[1] = (DWORD) strtoul(unid_buffer+16, NULL, 16); unid_buffer[16] = '\0'; /* DB part first */ unid.File.Innards[0] = (DWORD) strtoul(unid_buffer+8, NULL, 16); unid_buffer[8] = '\0'; unid.File.Innards[1] = (DWORD) strtoul(unid_buffer, NULL, 16); sError = NSFNoteOpenByUNID(hDB, &unid, (WORD) 0, &hNote); if (sError != NOERROR) { AddInLogMessageText("Did not handle '%s' due to UNID '%s'", sError, modstr, unid_buffer); /* Close database handle */ NSFDbClose(hDB); return (kFilterNotHandled); } sError = NSFDbPathGet(hDB, filepath, NULL); /* Start with the general HTTP response header */ strcpy(retbuffer, "HTTP/1.1 200 OK\r\n"); strcat(retbuffer, "Server: DisplayLinks on Domino\r\n"); strcat(retbuffer, "Date: "); if (!ConvertTIMEDATEToSMTPDate(&timedate, retbuffer+strlen(retbuffer))) strcat(retbuffer, "Mon, 29 Jun 2015 16:45:55 GMT"); strcat(retbuffer, "\r\n"); if (action == DL_DISPLAY_AS_JSON) strcat(retbuffer,"Content-type: application/json; charset=UTF-8\r\n"); else strcat(retbuffer,"Content-type: text/html; charset=UTF-8\r\n"); strcat(retbuffer,"Transfer-Encoding: chunked\r\n"); strcat(retbuffer,"\r\n"); reserved = 0; ret = (context->WriteClient)(context, retbuffer, (unsigned int) strlen((char *)retbuffer), reserved, &errid); /* Now, get the $Links value from the note and start cycling through the content */ sError = NSFItemInfo(hNote, ITEM_NAME_LINK, /* "$Links" */ (WORD) strlen(ITEM_NAME_LINK), &bidLinksItem, &wLinksType, &bidLinksValue, &dwLinksValueLen); retstr = retbuffer; if (action == DL_DISPLAY_AS_JSON) strcpy(retbuffer, "{\"DisplayLinks\": {\r\n"); else { strcpy(retstr, ""); strcat(retstr, "Doclinks\r\n"); strcat(retstr, "\r\n"); strcat(retstr, "

"); retstr += strlen(retstr); if (NSFItemGetText(hNote, "Subject", retstr, 63) > 0 || NSFItemGetText(hNote, "Title", retstr, 63) > 0) { for (k = 0; k < strlen(retstr); k++) { if (!isalnum(retstr[k]) && retstr[k] != '-' && retstr[k] != ':' && retstr[k] != ',' && retstr[k] != '.') retstr[k] = ' '; } strcat(retstr, " - related documents

\r\n"); retstr += strlen(retstr); } else if (action != DL_DISPLAY_AS_JSON) strcpy(retstr, "No Subject found\r\n"); } if (sError != NOERROR) { if (action == DL_DISPLAY_AS_HTML) strcpy(retstr, "
No $Links information
\r\n"); } else { /* NOTE: $Links is NOT canonical, so ODS functions should */ /* not be used to read these structures */ pLinksValue = OSLockBlock(BYTE, bidLinksValue); pLinksValue += sizeof(WORD); /* pLinksValue now points to a LIST data structure. Get it. */ memcpy ((char *) &List, pLinksValue, sizeof(LIST)); strcpy(servername, ""); strcpy(webservername, ""); if (List.ListEntries == 0) { strcpy(retstr, "
No $Links information
\r\n"); } else { sError = NSFDbReplicaInfoGet(hDB, &repinfo); OSGetEnvironmentString ("ServerName", servername, (WORD)131); if ((str = strchr(servername, '/')) != NULL) *str = '\0'; strcpy(webservername, "http://"); OSGetEnvironmentString ("DisplayLinksServer", webservername+strlen(webservername), (WORD)(131-strlen(webservername))); if (strlen(webservername) <= 8) strcpy(webservername, "http:"); } pLinksValue += sizeof(LIST); retstr += strlen(retstr); count = 0; if (action == DL_DISPLAY_AS_JSON) { strcpy(retstr, "\"links\": [ \r\n"); retstr += strlen(retstr); } for (i = 0; i < List.ListEntries; i++) { count++; notelink_ = (NOTELINK *)pLinksValue; pLinksValue += sizeof(NOTELINK); if (notelink_->Note.Note.Innards[0] == 0L && notelink_->Note.Note.Innards[1] == 0L) { if (notelink_->View.Note.Innards[0] == 0L && notelink_->View.Note.Innards[1] == 0L) { /* Db link */ if (action == DL_DISPLAY_AS_JSON) { sprintf(retstr, "%s{\"replica\": \"%.8lx%.8lx\", \"viewUNID\": \"\", \"UNID\": \"\", \"foundText\": ", (count > 1 ? ", " : ""), notelink_->File.Innards[1], notelink_->File.Innards[0]); } else { sprintf(retstr, "
Notes db link:   Web link:   ", servername, notelink_->File.Innards[1], notelink_->File.Innards[0], webservername, filepath); } retstr += strlen(retstr); if (notelink_->File.Innards[0] == repinfo.ID.Innards[0] && notelink_->File.Innards[1] == repinfo.ID.Innards[1]) { sError = NSFDbInfoGet(hDB, buffer); if (sError != NOERROR) strcat(retstr, "Current database, could not retrieve title"); else { NSFDbInfoParse(buffer, INFOPARSE_TITLE, retstr, NSF_INFO_SIZE-1); } } else { strcat(retstr, "Different database, did not retrieve title"); } } else { /* View link */ if (action == DL_DISPLAY_AS_JSON) { sprintf(retstr, "%s{\"replica\": \"%.8lx%.8lx\", \"viewUNID\": \"%.8lx%.8lx%.8lx%.8lx\", \"UNID\": \"\", , \"foundText\": ", (count > 1 ? ", " : ""), notelink_->File.Innards[1], notelink_->File.Innards[0], notelink_->View.File.Innards[1], notelink_->View.File.Innards[0], notelink_->View.Note.Innards[1], notelink_->View.Note.Innards[0]); } else { sprintf(retstr, "
Notes view link:   Web link:   ", servername, notelink_->File.Innards[1], notelink_->File.Innards[0], notelink_->View.File.Innards[1], notelink_->View.File.Innards[0], notelink_->View.Note.Innards[1], notelink_->View.Note.Innards[0], webservername, filepath, notelink_->View.File.Innards[1], notelink_->View.File.Innards[0], notelink_->View.Note.Innards[1], notelink_->View.Note.Innards[0]); } if (notelink_->File.Innards[0] == repinfo.ID.Innards[0] && notelink_->File.Innards[1] == repinfo.ID.Innards[1]) { sError = NSFNoteOpenByUNID(hDB, &(notelink_->View), (WORD) 0, &hNote2); if (sError != NOERROR) strcat(retstr, "View in current database, could not retrieve title"); else { NSFItemGetText(hNote2, FIELD_TITLE, retstr, 132); NSFNoteClose(hNote2); hNote2 = (NOTEHANDLE) NULLHANDLE; strcat(retstr, " (view)"); } } else { strcat(retstr, "View in different database"); } } } else { /* Note link */ if (action == DL_DISPLAY_AS_JSON) { sprintf(retstr, "%s{\"replica\": \"%.8lx%.8lx\", \"viewUNID\": \"%.8lx%.8lx%.8lx%.8lx\", \"UNID\": \"%.8lx%.8lx%.8lx%.8lx\", \"foundText\": ", (count > 1 ? ", " : ""), notelink_->File.Innards[1], notelink_->File.Innards[0], notelink_->View.File.Innards[1], notelink_->View.File.Innards[0], notelink_->View.Note.Innards[1], notelink_->View.Note.Innards[0], notelink_->Note.File.Innards[1], notelink_->Note.File.Innards[0], notelink_->Note.Note.Innards[1], notelink_->Note.Note.Innards[0]); } else { if (notelink_->View.Note.Innards[0] == 0 && notelink_->View.Note.Innards[1] == 0) { sprintf(retstr, "
Notes doclink:   Web link:   ", servername, notelink_->File.Innards[1], notelink_->File.Innards[0], notelink_->Note.File.Innards[1], notelink_->Note.File.Innards[0], notelink_->Note.Note.Innards[1], notelink_->Note.Note.Innards[0], webservername, filepath, notelink_->Note.File.Innards[1], notelink_->Note.File.Innards[0], notelink_->Note.Note.Innards[1], notelink_->Note.Note.Innards[0]); } else { sprintf(retstr, "
Notes doclink:   Web link:   ", servername, notelink_->File.Innards[1], notelink_->File.Innards[0], notelink_->View.File.Innards[1], notelink_->View.File.Innards[0], notelink_->View.Note.Innards[1], notelink_->View.Note.Innards[0], notelink_->Note.File.Innards[1], notelink_->Note.File.Innards[0], notelink_->Note.Note.Innards[1], notelink_->Note.Note.Innards[0], webservername, filepath, notelink_->View.File.Innards[1], notelink_->View.File.Innards[0], notelink_->View.Note.Innards[1], notelink_->View.Note.Innards[0], notelink_->Note.File.Innards[1], notelink_->Note.File.Innards[0], notelink_->Note.Note.Innards[1], notelink_->Note.Note.Innards[0]); } } if (action == DL_DISPLAY_AS_JSON) strcat(retstr, "\""); else strcat(retstr, ""); retstr += strlen(retstr); if (notelink_->File.Innards[0] == repinfo.ID.Innards[0] && notelink_->File.Innards[1] == repinfo.ID.Innards[1]) { sError = NSFNoteOpenByUNID(hDB, &(notelink_->Note), (WORD) 0, &hNote2); if (sError != NOERROR) strcat(retstr, "Document in current database, could not retrieve subject"); else { if (NSFItemGetText(hNote2, "Subject", retstr, 63) == 0) { if (NSFItemGetText(hNote2, "Title", retstr, 63) == 0) strcat(retstr, "Document in current database, no subject found"); else strcat(retstr, ""); } NSFNoteClose(hNote2); hNote2 = (NOTEHANDLE) NULLHANDLE; } for (k = 0; k < strlen(retstr); k++) { if (!isalnum(retstr[k]) && retstr[k] != '-' && retstr[k] != ':' && retstr[k] != ',' && retstr[k] != '.') retstr[k] = ' '; } } else strcat(retstr, "Document in different database, did not retrieve subject"); } if (action == DL_DISPLAY_AS_JSON) strcat(retstr, "\"}"); else strcat(retstr, "
"); len = (unsigned int) strlen(retbuffer); /* If we are approaching the full buffer, write this and start over */ if (len > 3800) { /* Length of this chunk */ sprintf(buffer, "%x\r\n", (unsigned int)len); ret = (context->WriteClient)(context, buffer, (unsigned int) strlen(buffer), reserved, &errid); /* Write out the chunk and terminating characters */ ret = (context->WriteClient)(context, retbuffer, (unsigned int)len, reserved, &errid); ret = (context->WriteClient)(context, "\r\n", (unsigned int) 2, reserved, &errid); memset(retbuffer, '\0', sizeof(retbuffer)); retstr = retbuffer; } else retstr += strlen(retstr); } } if (action == DL_DISPLAY_AS_JSON) sprintf(retbuffer+strlen(retbuffer), "],\r\n\"Count\":%ld\r\n}}", (long) count); else strcat(retbuffer, "\r\n"); len = (unsigned int) strlen(retbuffer); /* Length of this chunk */ sprintf(buffer, "%x\r\n", (unsigned int)len); ret = (context->WriteClient)(context, buffer, (unsigned int) strlen(buffer), reserved, &errid); /* Write out the chunk and terminating characters */ ret = (context->WriteClient)(context, retbuffer, (unsigned int)len, reserved, &errid); ret = (context->WriteClient)(context, "\r\n", (unsigned int) 2, reserved, &errid); /* Final empty chunk */ ret = (context->WriteClient)(context, "0\r\n\r\n", (unsigned int) 2, reserved, &errid); return (kFilterHandledRequest); } BOOL ConvertTIMEDATEToSMTPDate(TIMEDATE *timedate_, char *ret_buffer) { INTLFORMAT intl_format; TIME time_to_use; int hour_val; int min_val; ret_buffer[0] = '\0'; OSGetIntlSettings(&intl_format, sizeof(intl_format)); time_to_use.GM = *timedate_; if (TimeGMToLocal(&time_to_use)) return(FALSE); switch (time_to_use.weekday) { case 1: strcpy (ret_buffer, "Sun"); break; case 2: strcpy (ret_buffer, "Mon"); break; case 3: strcpy (ret_buffer, "Tue"); break; case 4: strcpy (ret_buffer, "Wed"); break; case 5: strcpy (ret_buffer, "Thu"); break; case 6: strcpy (ret_buffer, "Fri"); break; case 7: strcpy (ret_buffer, "Sat"); break; default: strcpy (ret_buffer, "???"); break; } sprintf(ret_buffer+strlen(ret_buffer), ", %2d ", (int) time_to_use.day); switch (time_to_use.month) { case 1: strcat (ret_buffer, "Jan"); break; case 2: strcat (ret_buffer, "Feb"); break; case 3: strcat (ret_buffer, "Mar"); break; case 4: strcat (ret_buffer, "Apr"); break; case 5: strcat (ret_buffer, "May"); break; case 6: strcat (ret_buffer, "Jun"); break; case 7: strcat (ret_buffer, "Jul"); break; case 8: strcat (ret_buffer, "Aug"); break; case 9: strcat (ret_buffer, "Sep"); break; case 10: strcat (ret_buffer, "Oct"); break; case 11: strcat (ret_buffer, "Nov"); break; case 12: strcat (ret_buffer, "Dec"); break; default: strcat (ret_buffer, "???"); break; } sprintf(ret_buffer+strlen(ret_buffer), " %4d %2d:%2d:%2d ", (int) time_to_use.year, (int) time_to_use.hour, (int) time_to_use.minute, (int) time_to_use.second); time_to_use.zone = -time_to_use.zone; if (time_to_use.zone < 0 && time_to_use.zone < 99) { min_val = time_to_use.zone / 100; hour_val = -(time_to_use.zone + (min_val*100)); } else if (time_to_use.zone > 0 && time_to_use.zone > 99) { min_val = time_to_use.zone / 100; hour_val = time_to_use.zone - (min_val*100); } else { hour_val = time_to_use.zone; min_val = 0; } if (hour_val > 0 && time_to_use.zone < 0) hour_val = -hour_val; if (time_to_use.dst) { hour_val++; if (hour_val > 11) hour_val = 0; } sprintf(ret_buffer+strlen(ret_buffer), "%+03d%02d", (int) hour_val, (int) min_val); return(TRUE); } BOOL IsAllHex(char *str, USHORT len) { USHORT i; for (i = 0; i < len && str[i] != '\0'; i++) { if ((str[i] >= '0' && str[i] <= '9') || (str[i] >= 'a' && str[i] <= 'f') || (str[i] >= 'A' && str[i] <= 'F')) { /* Acceptable hex so far */ } else return (FALSE); } /* All acceptable */ return (TRUE); } char * stristr (char *buffer, char *substr) { unsigned int len; char *str; if (buffer == NULL || substr == NULL) return (NULL); str = buffer; len = (unsigned int) strlen(substr); while (*str != '\0' && _strnicmp(str, substr, len) != 0) str++; if (*str == '\0') return (NULL); else return (str); } #ifdef __cplusplus } #endif /* ----- end of dsplnks.cpp */