source: trunk/src/reporting/gmewhtmlemail/gmewhtmlemail.c @ 8084

Revision 8084, 197.7 KB checked in by paulf, 12 months ago (diff)

updated ewave plotter option to fix scaling and added MaxDist? element

  • Property svn:executable set to *
Line 
1/******************************************************************************
2 *                                GMEWHTMLEMAIL                               *
3 *                                                                            *
4 * Simplified graphical email alert for earthworm using google APIs.          *
5 *                                                                            *
6 *                                                                            *
7 * Description:                                                               *
8 * Produces an web file (html) containing graphical information on detected   *
9 * earthquakes. The presented graphical information consists on a map of the  *
10 * computed hypocenter and reporting stations as well as the seismic traces   *
11 * retrieved from a WaveServer. The graphical information is produced using   *
12 * google maps and google charts APIs. As a consequence, correct visualization*
13 * of the web file requires an internet connection. The web files may be sent *
14 * to a set of recipients via email. In this case, gmewhtmlemail uses         *
15 * sendmail, which is common with several linux distributions.                *
16 * The code is a combination of seisan_report to retrieve hyp2000arc          *
17 * messages, and gmew to access and retrieve data from a set of waveservers.  *
18 * The code includes custom functions to generate the requests to the google  *
19 * APIs and embeded these on the output html file.                            *
20 * Besides the typical .d configuration file, gmewhtmlemail requires a css    *
21 * file with some basic style configurations to include in the output html.   *
22 *                                                                            *
23 * Written by R. Luis from CVARG in July, 2011                                *
24 * Contributions and testing performed by Jean-Marie Saurel, from OVSM/IPGP   *
25 * Later modifications for features and blat, by Paul Friberg                 *
26 * ISTI converted to gmewhtmlemail for the US Bureau of Reclamation in 2016
27 *                                                                            *
28 * Two important URLs to follow:                                              *
29 *    https://developers.google.com/maps/documentation/staticmaps/            *
30 *    https://developers.google.com/chart/interactive/docs/reference/         *
31 *****************************************************************************/
32
33#define VERSION_STR "1.9.0 - 2019-10-07"
34
35#define MAX_STRING_SIZE 1024    /* used for many static string place holders */
36#define CM_S_S_1_g 980          /* how many cm/s/s in 1 g, used for conversion */
37
38/* Includes
39 **********/
40#include <ctype.h>
41#include <math.h>
42#include <stdio.h>              /*  pclose(),  popen() on *nix */
43                                /* _pclose(), _popen() on MSVC */
44#include <stdint.h>             /* uint16_t, uint32_t, uint64_t, UINT64_C() */
45#include <stdlib.h>
46#include <string.h>             /* strtok_r() on *nix */
47                                /* strtok_s() on MSVC */
48#if !defined(_WIN32) && !defined(_WIN64)
49#include <unistd.h>
50#endif
51
52#if defined(_WIN32) || defined(_WIN64)
53#define pclose     _pclose      /* _pclose()   has the same signature as pclose()   */
54#define popen      _popen       /* _popen()    has the same signature as popen()    */
55#define strtok_r    strtok_s    /*  strtok_s() has the same signature as strtok_r() */
56#endif
57
58#include "geo.h"
59#include "earthworm.h"
60#include "transport.h"
61#include "ws_clientII.h"
62#include "trace_buf.h"
63#include "swap.h"
64#include "kom.h"
65#include "read_arc.h"
66/* #include "rw_mag.h" */
67#include "rw_strongmotionII.h"
68#include "site.h"
69#include "chron3.h"
70#include "gd.h"
71#include "gdfonts.h"
72#include "fleng.h"
73#include "ioc_filter.h"         /*header containing filtering functions*/
74#include "kml_event.h"
75#include "sqlite3.h"
76#include "time_ew.h"            /* gmtime_ew(), localtime_ew() */
77
78char neic_id[30];
79
80/* Defines
81 *********/
82#define MAX_STATIONS 200
83#define MAX_STREAMS 1024        /* Max number of stream conversion factors to process */
84#define MAX_GET_SAMP 2400       /* Number of samples in a google chart */
85#define MAX_REQ_SAMP 600        /* Maximum number of samples per google request */
86#define MAX_GET_CHAR 5000       /* Maximum number of characters in a google GET request */
87#define MAX_POL_LEN 120000      /* Maximum number of samples in a trace gif */
88#define MAX_WAVE_SERVERS 10
89#define MAX_COLOR_PGAS 10
90#define MAX_ADDRESS 80
91#define MAX_PORT 6
92#define MAX_EMAIL_CHAR 160
93#define DEFAULT_SUBJECT_PREFIX "EWalert"
94#define DEFAULT_GOOGLE_STATIC_MAPTYPE "hybrid"
95#define DEFAULT_MAPQUEST_STATIC_MAPTYPE "hyb"
96#define OTHER_WORLD_DISTANCE_KM 100000000.0
97#define DUMMY_MAG 9.0
98#define KM2MILES 0.621371
99#define MAX_SM_PER_EVENT    40
100#define MAX_REGIONS     20
101#define MAX_AREAS       50
102#define MAX_SUB_DIST    100
103#define MAX_SUB_DAMS    1
104#define BEFORE_MAX_COLOR    "F0F0F0"
105#define AFTER_MAX_COLOR    "FFFFFF"
106#define font_open "<font size=\"1\" face=\"Sans-serif\">"
107#define font_close "</font>"
108#define MAX_SCNL_STRING 32 /* max size of a SCNL string */
109
110/* Structures
111 ************/
112
113// Waveservers
114typedef struct {
115    char wsIP[MAX_ADDRESS];
116    char port[MAX_PORT];
117} WAVESERV;
118
119typedef struct {
120    double mag;
121    double dist;
122} MAGDIST;
123
124// Email Recipients
125// TODO: Upgrade to have different parameters for each email recipient
126typedef struct {
127    char address[MAX_EMAIL_CHAR];
128    char subscription;
129    char sendOnEQ;
130    char sendOnSM;
131    double min_magnitude;
132    double max_distance;
133    int numRegions;
134    int numAreas;
135    int numDams;
136    uint32_t subRegions;
137    uint64_t subAreas;
138    uint16_t subDams[MAX_SUB_DAMS];
139    int shouldSendEmail;         //flag indicating whether or not email is to be sent to this address
140} EMAILREC;
141
142typedef struct HYPOARCSM2 {
143    HypoArc ha;
144    SM_INFO sm_arr[MAX_SM_PER_EVENT];
145    int     sm_count;
146    time_t  deliveryTime;
147    char    fromArc;
148    char    sncl[MAX_SCNL_STRING];
149} HypoArcSM2;
150
151typedef struct site_order {
152    double  key;
153    int     idx;
154} SiteOrder;
155
156typedef struct SNCLConv {
157        char SNCL[20];
158        double convFactor;
159} Conv;
160
161typedef struct dam_info {
162    int    dam_id;
163    int    region_id;
164    int    area_id;
165    double lat;
166    double lon;
167    double dist;
168    char   name[200];
169    char   station[10];
170} DamInfo;
171
172char region_names[MAX_REGIONS][MAX_STRING_SIZE] = {""};
173/*,
174    "Great Plains GP",
175    "Lower Colorado LC",
176    "Mid-Pacific MP",
177    "Pacific Northwest PN",
178    "Upper Colorado UC" };*/
179
180char region_abbrs[MAX_REGIONS][5] = {""}; //,"GP","LC", "MP","PN", "UC" };
181
182int region_order[MAX_REGIONS] = {-1}; //,1,2,3,4,5,99};
183
184char area_abbrs[MAX_AREAS][5] = {""};
185/*    "","DK","EC","MT","NK","OT","WY","LCD","PX","SC",
186    "Y","CC","KB","LB","NC","SCC","LC","SR","UC","A",
187    "P","WC"}; */
188int area_order[MAX_AREAS] = {-1}; //,19,11,1,2,12,13,16,7,3,14,4,5,20,8,9,15,17,18,21,6,10,99};
189char area_names[MAX_AREAS][MAX_STRING_SIZE] = {""};
190
191/* Functions in this file
192 ************************/
193void config(char*);
194void lookup(void);
195void status( unsigned char, short, char* );
196int process_message( int arc_index ); //SM_INFO *sm ); //, MAG_INFO *mag, MAG_INFO *mw);
197void MapRequest(char*, SITE**, int, double, double, HypoArcSM2*, SiteOrder*, SiteOrder* );
198void GoogleMapRequest(char*, SITE**, int, double, double, HypoArcSM2*, SiteOrder*, SiteOrder* );
199void MapQuestMapRequest(char*, SITE**, int, double, double, HypoArcSM2*, SiteOrder*, SiteOrder* );
200char simpleEncode(int);
201int searchSite(char*, char*, char*, char*);
202char* getWSErrorStr( int, char* );
203int getWStrbf( char*, int*, SITE*, double, double );
204int trbufresample( int*, int, char*, int, double, double, int, double* );
205double fbuffer_add( double*, int, double );
206int createGifPlot(char *chartreq, HypoArc *arc, char *buffer, int bsamplecount, SITE *waveSite, double starttime, double endtime, char phaseName[2]);
207int writePlotRequest(char *, size_t, long, char *, char *, char *, char *, char *, char *, time_t, int); 
208int makeGoogleChart( char*, int*, int, char *, double, int, int, double* );
209void gdImagePolyLine( gdImagePtr, gdPointPtr, int, int );
210int trbf2gif( char*, int, gdImagePtr, double, double, int, int );
211int pick2gif(gdImagePtr, double, char *, double, double, int);
212int gmtmap(char *request,SITE **sites, int nsites,double hypLat, double hypLon,int idevt);
213void InsertShortHeaderTable(FILE *htmlfile, HypoArc *arc, char Quality);
214void InsertHeaderTable(FILE *htmlfile, HypoArc *arc, char Quality, int showDM, char *, SM_INFO*);
215int InsertStationTable(FILE *, char, int, SiteOrder*, SITE**, SM_INFO **, int *, SiteOrder*, double*);
216int InsertDamTable(FILE *htmlfile, HypoArc *arc, double *max_epga_g);
217unsigned char* base64_encode( size_t* output_length, const unsigned char *data, size_t input_length );
218double deg2rad(double deg);
219double rad2deg(double rad);
220double distance(double lat1, double lon1, double lat2, double lon2, char unit);
221char* read_disclaimer( char* path );
222int formatTimestamps( time_t t, double lat, double lon, char* timestr, char* timestrUTC );
223double EstimatePGA( double lat, double lon, double evt_lat, double evt_lon, double evt_depth, double evt_mag );
224int compareDamOrder( const void*dam_p_1, const void*dam_p_2 );
225int shouldSendEmail( int i, double distance_from_center, HypoArc *arc, HypoArcSM2 *arcsm, uint32_t subRegionsUsed, uint64_t subAreasUsed );
226void prepare_dam_info( HypoArc *arc, char* regionsStr, char* regionsUsed, uint32_t* subRegionsUsed, uint64_t* subAreasUsed, int MaxDist);
227int conversion_read(char *path);
228
229/* Globals
230 *********/
231static SHM_INFO   InRegion; // shared memory region to use for input
232static pid_t      MyPid;    // Our process id is sent with heartbeat
233
234/* Things to read or derive from configuration file
235 **************************************************/
236static int        LogSwitch;              // 0 if no logfile should be written
237static long       HeartbeatInt;           // seconds between heartbeats
238static long       MaxMessageSize = 4096;  // size (bytes) of largest msg
239static int        Debug = 0;              // 0=no debug msgs, non-zero=debug
240static int        DebugEmail = 0;              // 0=no debug msgs, non-zero=debug
241static MSG_LOGO  *GetLogo = NULL;         // logo(s) to get from shared memory
242static short      nLogo = 0;              // # of different logos
243static int        MAX_SAMPLES = 60000;    // Number of samples to get from ws
244static long       wstimeout = 5;          // Waveserver Timeout in Seconds
245static char       HTMLFile[MAX_STRING_SIZE];          // Base name of the out files
246static char       EmailProgram[MAX_STRING_SIZE];      // Path to the email program
247//static char       StyleFile[MAX_STRING_SIZE];         // Path to the style (css) file
248static char       SubjectPrefix[MAX_STRING_SIZE];     // defaults to EWalert, settable now
249static char       KMLdir[MAX_STRING_SIZE];          // where to put the kml files, dir must exist
250static char       KMLpreamble[MAX_STRING_SIZE];          // where to find the KML preamble needed for this.
251static int        nwaveservers = 0;       // Number of waveservers
252static int        ncolorpga = 0;          // Number of color PGA values
253static int        nemailrecipients = 0;   // Number of email recipients
254static int        nStaticEmailRecipents = 0;
255static int        NoWaveformPlots = 0;    // set to 1 to turn off waveform plots (google static charts of time-series)
256static double     TimeMargin = 10;        // Margin to download samples.
257static double     DurationMax = 144.;     // Maximum duration to show., truncate to this if greater (configurable)
258static long       EmailDelay = 30;        // Time after reciept of trigger to send email
259static int        UseBlat = 0;        // use blat syntaxi as the mail program instead of a UNIX like style
260static int        UseRegionName = 0;        // put Region Name from FlynnEnghdal region into table at top, off by default
261static char       BlatOptions[MAX_STRING_SIZE];      // apply any options needed after the file....-server -p profile etc...
262static char       StaticMapType[MAX_STRING_SIZE];      // optional, specification of google map type
263static int        UseUTC = 0;         // use UTC in the timestamps of the html page
264static int        DontShowMd = 0;         // set to 1 to not show Md value
265static int        DontUseMd = 0;         // set to 1 to not use Md value in MinMag decisions
266static int        ShowDetail = 0;     // show more detail on event
267static int        ShortHeader = 0;     // show a more compact header table
268static char       MinQuality = 'D'; // by default send any email with quality equal to or greater than this
269static char       DataCenter[MAX_STRING_SIZE];  // Datacenter name to be presented in the resume table.(optional)
270static int        SPfilter = 0;                       // Shortperiod filtering (optional)
271static char       Cities[MAX_STRING_SIZE];          //cities filepath to be printed with gmt in the output map
272static int        GMTmap=0;                     //Enable GMT map generation (Optional)
273static int        StationNames=0;                 // Station names in the GMT map (Optional if GMTmap is enable)  (Optional)
274static char       MapLegend[MAX_STRING_SIZE];       // Map which contains the map legend to be drawn in the GMT map (optional)
275static int        Mercator=1;                            // Mercator projection. This is the default map projection.
276static int        Albers=0;                          // Albers projection.
277static int        MaxStationDist = 100;       // Maximum distance (in km) from origin for stations to appear in email
278static int        MaxFacilityDist = 100;       // Maximum distance (in km) from origin for facilities to appear in email
279static char       db_path[MAX_STRING_SIZE] = {0};                      // absolute path to sqlite DB file
280static char       dbAvailable;              // 0 if quake ID trabnsaltion DB not available
281static double     center_lat = 0.0;  // for distance check from center point
282static double     center_lon = 0.0;  // for distance check from center point
283static int        ShowMiles = 0;       // display distainces in miles as well as km
284static int        MaxArcAge = 0;       // ignore Arc message more than this many secs old (0 means take all)
285static char*      EarthquakeDisclaimer = NULL;
286static char*      TriggerDisclaimer = NULL;
287static int        PlotAllSCNLs = 0;     // Plot all SCNLs; default is false (plot max SCNL for each SNL)
288static char       MapQuestKey[MAX_STRING_SIZE] = {0};
289static char       MQStaticMapType[MAX_STRING_SIZE];      // optional, specification of MapQuest map type
290static int        facility_font_size = 3;
291static int        ShowRegionAndArea = 0;
292static int        showMax = 1;
293static double     IgnoreHours = 0;
294static char       PathToPython[MAX_STRING_SIZE] = {0};
295static char       PathToLocaltime[MAX_STRING_SIZE] = {0};
296static char       PathToEventEmail[MAX_STRING_SIZE] = {0};
297static char       ReportEstimatedPGAs = 0;
298static char       EstimatePGAsPath[MAX_STRING_SIZE] = {0};
299static char       SubscriptionPath[MAX_STRING_SIZE] = {0};
300static double     SubscriptionTimestamp = 0;
301static double     PGAThreshold = 0.0001; /* (note this is in cm/s/s) */
302static int        MaxFacilitiesInTable = -1;
303static double     IgnorePGABelow = 0.01;
304static Conv       ConversionFactors[MAX_STREAMS] = {{{0}}};
305
306//static char       dam_info_kind = 0;
307
308/* RSL: 2012.10.10 Gif options */
309static int        UseGIF = 0;            // Use GIF files instead of google charts
310static int        TraceWidth = 600;        // Width of the GIF files
311static int        TraceHeight = 61;        // Height of the GIF files
312
313static int        UseEWAVE = 0;                 //Use the EWAVE plotting service to generate images
314static char       ewaveAddr[64];
315static char       ewavePort[16];
316
317//Option to restrict thresholds to a particular network:
318static int               RestrictThresholds = 0;
319static char      RestrictedNetwork[8] = {0};
320
321// Array of waveservers
322static WAVESERV   waveservers[MAX_WAVE_SERVERS];
323// Array of Color PGA values and colors
324static double colorpga_values[MAX_COLOR_PGAS];
325static char   colorpga_colors[MAX_COLOR_PGAS][MAX_STRING_SIZE];
326// Array of email recipients
327static EMAILREC   *emailrecipients = NULL;
328
329#define NMAGALLOC 10   /* the number of MagDist elements statically allocated, we shouldn't need more than this */
330static int NMagDistAlloc = NMAGALLOC;   /* the number of MagDist override elements alloced */
331static int NMagDist = 0;        /* the number of MagDist override elements used */
332static MAGDIST MagDist[NMAGALLOC]; /* static array of MagDist elements */
333
334/* Things to look up in the earthworm.h tables with getutil.c functions
335 **********************************************************************/
336static long               InRingKey = 0;    // key of transport ring for input
337static unsigned char      InstId = 0;       // local installation id
338static unsigned char      MyModId = 0;      // Module Id for this program
339static unsigned char      TypeHeartBeat = 0;
340static unsigned char      TypeError = 0;
341static unsigned char      TypeHYP2000ARC = 0;
342static unsigned char      TypeTraceBuf2 = 0;
343static unsigned char      TypeSTRONGMOTIONII = 0;
344static unsigned char      TypeTHRESH = 0;
345
346
347/* Error messages used by gmewhtmlemail
348 ************************************/
349#define  ERR_MISSGAP       0   // sequence gap in transport ring         
350#define  ERR_MISSLAP       1   // missed messages in transport ring     
351#define  ERR_TOOBIG        2   // retreived msg too large for buffer     
352#define  ERR_NOTRACK       3   // msg retreived; tracking limit exceeded
353static char  Text[150];        // string for log/error messages
354
355#define MAX_QUAKES  100 // should make this configurable at some point
356#define MAX_MAG_CHANS 1000  // this SHOULD be enough
357#define MAX_DAMS  600 // number of facilities allowed in csv file, should be made config later
358
359HypoArcSM2       *arc_list[MAX_QUAKES];     // arc_list[0..arc_counter-1] point to elements of arc_data
360HypoArcSM2       arc_data[MAX_QUAKES];
361long             arc_counter = 0;
362
363static time_t next_delivery = -1;           // When to report on arc_list[next_arc]
364static int next_arc = -1;
365
366DamInfo dam_tbl[MAX_DAMS];
367int dam_count = 0, dam_close_count = 0, dam_sub_close_count = 0;
368DamInfo *dam_order[MAX_DAMS];
369int damMapLimit=25;
370
371void free_arcsm2( HypoArcSM2 *p ) {
372    if ( p->sm_count > 0 ) {
373        //_free( p->sm_arr, "sm list" );
374    }
375    if ( arc_list[arc_counter-1] != p ) {
376        *p = arc_data[arc_counter-1];
377    }
378    //_free( p, "sm2" );
379}
380
381char* my_strtok_r( char **brkt_ptr ) {
382    if ( **brkt_ptr == ',' ) {
383        **brkt_ptr = 0;
384        return (*brkt_ptr)++;
385    } else
386        return strtok_r( NULL, ",", brkt_ptr );
387}
388
389int region_compare( const void* a, const void* b ) {
390    int ia = *((int*)a), ib = *((int*)b);
391    return strcmp( region_abbrs[ia], region_abbrs[ib] );
392}
393
394int area_compare( const void* a, const void* b ) {
395    int ia = *((int*)a), ib = *((int*)b);
396    return strcmp( area_abbrs[ia], area_abbrs[ib] );
397}
398
399//reads stream conversion factor file
400//initializes ConversionFactor array
401//returns 0 if successful, -1 otherwise
402int conversion_read( char *path ) {
403        FILE *fp = NULL;
404        int lNum = 0;
405        size_t sLen = 0;
406        char factorStr[20];
407        char lBuff[80];
408        char *s1 = NULL;
409        char *s2 = NULL;
410
411        if (!(fp = fopen( path, "r" ))) {
412                logit("et", "Error opening stream conversion factor file %s\n", path); 
413                return -1;
414        }
415
416        for ( lNum = 0; (lNum < MAX_STREAMS) && (s1 = fgets(lBuff, 80, fp)); lNum++) {
417                if(!(s2 = strpbrk(s1, " \t"))) {
418                        //if a line can't be parsed, skip it:
419                        logit("et", "Error parsing line %s\n", lBuff);
420                        continue;       
421                }
422                sLen = s2 - s1;
423                strncpy(ConversionFactors[lNum].SNCL, s1, sLen); 
424                sLen = strcspn(s2, " \t"); //find num of whitespace chars to skip
425                strncpy(factorStr, (char *)&(s2[sLen]), 20);
426
427                ConversionFactors[lNum].convFactor = atof(factorStr);   
428        }
429
430        return 0;
431}
432
433int read_RAD_info( char *path ) {
434    int i, j, k, max_region = 0, max_area = 0;
435    FILE *fp;
436    char buffer[300], word[20];
437    char *line, *brkt;
438    int lineno = 0;
439    int stage = 0;  // 0=Regions, 1=Areas, 2=Dams
440
441    // Initialize region & area tables
442    for ( i=0; i<MAX_REGIONS; i++ ) {
443        region_order[i] = i;
444        strcpy( region_abbrs[i], "??" );
445    }
446    for ( i=0; i<MAX_AREAS; i++ ) {
447        area_order[i] = i;
448        strcpy( area_abbrs[i], "??" );
449    }
450    fp = fopen( path, "r" );
451    if ( fp == NULL ) {
452        logit("et", "Error opening RAD file %s\n", path);
453        return -1;
454    }
455    logit("et", "Reading RAD file from %s\n", path);
456
457    lineno++;
458    line = fgets(buffer, 290, fp);
459    if ( strncmp( line, "#Region", 6 ) ) {
460        logit("e","gmewhtmlemail: First line is not Regions header\n");
461        fclose(fp);
462        return -1;
463    }
464    lineno++;
465    line = fgets(buffer, 290, fp);
466
467        while ( line != NULL ) {
468        if ( strncmp( line, "#Area", 5 )==0 ) {
469            stage += 1;
470            lineno++;
471            line = fgets(buffer, 290, fp);
472            break;
473        }
474        i = atoi( strtok_r(line, ",", &brkt) );
475        if ( i >= MAX_REGIONS-1 ) {
476            logit("e","gmewhtmlemail: Maximum region code exceeded; exiting!\n");
477            fclose(fp);
478            return -1;
479        } else if ( i > max_region )
480            max_region = i;
481        strcpy( region_abbrs[i], my_strtok_r( &brkt ) );
482        strcpy( region_names[i], my_strtok_r( &brkt ) );
483        region_names[i][strlen(region_names[i])-1] = ' ';
484        strcat( region_names[i], region_abbrs[i] );
485        lineno++;
486        line = fgets(buffer, 290, fp);
487    }
488    max_region++;
489    region_order[max_region] = 99;
490    qsort( region_order+1, max_region-1, sizeof(int), region_compare );
491
492        if ( Debug ) {
493        logit("","Regions (%d):\n", max_region);
494        for ( i=1; i<max_region; i++ )
495            logit( "","   %2d (%-3s) '%s'\n", i, region_abbrs[i], region_names[i] );
496    }
497    while ( line != NULL ) {
498        if ( strncmp( line, "#Dams", 4 )==0 ) {
499            stage += 1;
500            lineno++;
501            line = fgets(buffer, 290, fp);
502            break;
503        }
504        for ( j = 0; line[j] && line[j] != ','; j++ );
505        line[j] = 0;
506        i = atoi( line );
507        if ( max_area >= MAX_AREAS-1 ) {
508            logit("e","gmewhtmlemail: Maximum area code exceeded; exiting!\n");
509            fclose(fp);
510            return -1;
511        } else if ( i > max_area )
512            max_area = i;
513        for ( j++, k=0; line[j] && line[j] != ','; j++, k++ )
514            area_abbrs[i][k] = line[j];
515        area_abbrs[i][k] = 0;
516        strcpy( area_names[i], line+j+1 );
517        area_names[i][strlen(area_names[i])-1] = 0;
518        lineno++;
519        line = fgets(buffer, 290, fp);
520    }
521    max_area++;
522    area_order[max_area] = 99;
523    qsort( area_order+1, max_area-1, sizeof(int), area_compare );
524    if ( Debug ) {
525        logit("","Areas (%d)!:\n", max_area);
526        for ( i=1; i<max_area; i++ )
527            logit("", "   %2d %-3s '%s'\n", i, area_abbrs[i], area_names[i] );
528    }
529    while ( line != NULL ) {
530        if ( dam_count >= MAX_DAMS ) {
531            logit("e","gmewhtmlemail: Maximum area code exceeded; exiting!\n");
532            fclose(fp);
533            return -1;
534        }
535        // example: MARTINEZ DAM,3,MP,SCC,38.01064137,-122.1084274,MRTZ
536        strcpy( dam_tbl[dam_count].name, strtok_r( line, ",", &brkt ) );
537        dam_tbl[dam_count].region_id = atoi( my_strtok_r(&brkt) );
538        my_strtok_r( &brkt );   // skip area abbrev
539        strcpy( word, my_strtok_r( &brkt ) );
540        for ( i=1; i<max_area; i++ )
541            if ( strcmp( word, area_abbrs[i] ) == 0 ) {
542                dam_tbl[dam_count].area_id = i;
543                break;
544            }
545        dam_tbl[dam_count].lat = atof( my_strtok_r(&brkt) );
546        dam_tbl[dam_count].lon = atof( my_strtok_r(&brkt) );
547        strcpy( dam_tbl[dam_count].station, my_strtok_r( &brkt ));
548        // remove cariage return and newlines from station name string
549        i = strlen(dam_tbl[dam_count].station);
550        if (dam_tbl[dam_count].station[i-1]=='\n') dam_tbl[dam_count].station[i-1]='\0';
551        i = strlen(dam_tbl[dam_count].station);
552        if (dam_tbl[dam_count].station[i-1]=='\r') dam_tbl[dam_count].station[i-1]='\0';
553        dam_tbl[dam_count].dam_id = dam_count;
554        dam_order[dam_count] = dam_tbl+dam_count;
555        dam_count++;
556        lineno++;
557        line = fgets(buffer, 290, fp);
558    }
559
560    if ( Debug ) {
561        logit("","Dams (%d):\n", dam_count);
562        for ( i=0; i<dam_count; i++ )
563            logit( "","   %2d %-3s %-3s (%10.4f,%10.4f) '%s' site=%s\n", i,
564                   region_abbrs[dam_tbl[i].region_id], area_abbrs[dam_tbl[i].area_id],
565                   dam_tbl[i].lon, dam_tbl[i].lat, dam_tbl[i].name, dam_tbl[i].station);
566    }
567    fclose( fp );
568    return 0;
569}
570
571int makeRoomForRecipients( int n ) {
572    int newSize = n > 0 ? nemailrecipients + n : -n;
573    emailrecipients = realloc( emailrecipients, newSize * sizeof( EMAILREC ) );
574    if ( emailrecipients != NULL ) {
575        nemailrecipients = newSize;
576        return 1;
577    } else
578        return 0;
579}
580
581char* assignMailingList() {
582    char* str = k_str();
583    if ( k_err() ) {
584        // There was no modifier; assign to both lists
585        emailrecipients[nemailrecipients-1].sendOnEQ = 1;
586        emailrecipients[nemailrecipients-1].sendOnSM = 1;
587    } else if ( strcmp( str, "EQK" ) == 0 ) {
588        emailrecipients[nemailrecipients-1].sendOnEQ = 1;
589        emailrecipients[nemailrecipients-1].sendOnSM = 0;
590    } else if ( strcmp( str, "SM" ) == 0 ) {
591        emailrecipients[nemailrecipients-1].sendOnEQ = 0;
592        emailrecipients[nemailrecipients-1].sendOnSM = 1;
593    } else {
594        return str;
595    }
596    return NULL;
597}
598
599void updateSubscriptions() {
600    FILE *fp;
601    char line[200], *line_pos, code[10];
602    int numSubs, i, ncodes, n, j;
603    double newTimeStamp;
604   
605    if ( SubscriptionPath[0] == 0 )
606        return;
607    fp = fopen( SubscriptionPath, "r" );
608    if ( fp == NULL )
609        return;
610    if ( fgets( line, 190, fp ) == NULL ) {
611        fclose( fp );
612        return;
613    }   
614    newTimeStamp = atof(line);   
615    if ( newTimeStamp <= SubscriptionTimestamp ) {
616        fclose( fp );
617        return;
618    }
619    if ( fgets( line, 190, fp ) == NULL ) {
620        fclose( fp );
621        return;
622    }       
623    numSubs = atoi(line);
624    if ( numSubs <= 0 ) {
625        fclose( fp );
626        return;
627    }
628    if ( makeRoomForRecipients( -(nStaticEmailRecipents + numSubs) ) == 0 ) {
629        fclose( fp );
630        return;
631    }
632    for ( ; numSubs > 0; numSubs-- ) {
633        if ( fgets( line, MAX_EMAIL_CHAR-1, fp ) == NULL ) {
634            nemailrecipients -= numSubs;
635            break;
636        }
637        emailrecipients[nemailrecipients-numSubs].subscription = 1;
638        line[strlen(line)-1] = 0;
639        strcpy( emailrecipients[nemailrecipients-numSubs].address, line );
640        if ( fgets( line, 190, fp ) == NULL ) {
641            nemailrecipients -= numSubs;
642            break;
643        } 
644        emailrecipients[nemailrecipients-numSubs].min_magnitude = atof( line );
645        if ( fgets( line, 190, fp ) == NULL ) {
646            nemailrecipients -= numSubs;
647            break;
648        } 
649        line_pos = line;
650        i = 0;
651        ncodes = 0;
652        emailrecipients[nemailrecipients-numSubs].subRegions = 0;
653        while ( 1 ) {
654            if ( isalnum( *line_pos ) )
655                code[i++] = *(line_pos++);
656            else { 
657                code[i++] = 0;
658                for ( i=0; region_abbrs[i][0]!=0 && strcmp(code,region_abbrs[i]); i++ );
659                if ( region_abbrs[i][0] ) {
660                    emailrecipients[nemailrecipients-numSubs].subRegions |= (1<<i);
661                }
662                if ( *line_pos == ',' ) { 
663                    i = 0;
664                    line_pos++;
665                } else
666                    break;
667            }
668        }
669        emailrecipients[nemailrecipients-numSubs].numRegions = ncodes;
670        if ( fgets( line, 190, fp ) == NULL ) {
671            nemailrecipients -= numSubs;
672            break;
673        } 
674        line_pos = line;
675        i = 0;
676        ncodes = 0;
677        emailrecipients[nemailrecipients-numSubs].subAreas = 0;
678        while ( 1 ) {
679            if ( isalnum( *line_pos ) )
680                code[i++] = *(line_pos++);
681            else {
682                code[i++] = 0;
683                for ( i=0; area_abbrs[i][0]!=0 && strcmp(code,area_abbrs[i]); i++ );
684                if ( area_abbrs[i][0] ) {
685                    emailrecipients[nemailrecipients-numSubs].subAreas |= (UINT64_C(1)<<i);
686                }
687                if ( *line_pos == ',' ) { 
688                    i = 0;
689                    line_pos++;
690                } else
691                    break;
692            }
693        }
694        emailrecipients[nemailrecipients-numSubs].numAreas = ncodes;       
695        if ( fgets( line, 190, fp ) == NULL ) {
696            nemailrecipients -= numSubs;
697            break;
698        } 
699        n = atoi(line);
700        emailrecipients[nemailrecipients-numSubs].numDams = 0;
701        for ( i=0; i < n; i++ ) {
702            if ( fgets( line, 190, fp ) == NULL ) {
703                nemailrecipients -= numSubs;
704                fclose( fp );
705                break;
706            } 
707            for ( j=0; strcmp(line,dam_tbl[i].name) && j<dam_count; j++ );
708            if ( j==dam_count ) {
709                logit("et","Unknown Dam for %s: '%s'\n", 
710                    emailrecipients[nemailrecipients-numSubs].address, line );
711            } else if ( emailrecipients[nemailrecipients-numSubs].numDams == MAX_SUB_DAMS ) {
712                logit("et","Too many dams for %s; ignoring '%s'\n", 
713                    emailrecipients[nemailrecipients-numSubs].address, line );
714            } else {
715                emailrecipients[nemailrecipients-numSubs].subDams[ emailrecipients[nemailrecipients-numSubs].numDams++ ] = i;
716            }
717        }
718    }   
719    fclose( fp );
720}
721
722static char* monthNames[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
723
724/* Main program starts here
725 **************************/
726int main(int argc, char **argv) {
727    time_t        timeNow;          // current time
728    time_t        timeLastBeat;     // time last heartbeat was sent
729    char         *msgbuf;           // buffer for msgs from ring
730    long          recsize;          // size of retrieved message
731    MSG_LOGO      reclogo;          // logo of retrieved message
732    unsigned char seq;
733    int           i, res;
734    HypoArcSM2    *arc;             // a hypoinverse ARC message
735
736    /* Check command line arguments
737    ******************************/
738    if (argc != 2)
739    {
740        fprintf(stderr, "Usage: gmewhtmlemail <configfile>\n");
741        fprintf(stderr, "Version: %s\n", VERSION_STR );
742        return EW_FAILURE;
743    }
744
745    /* Initialize name of log-file & open it
746    ***************************************/
747    logit_init(argv[1], 0, 256, 1);
748
749
750    /* Read the configuration file(s)
751    ********************************/
752    config(argv[1]);
753
754
755    /* Lookup important information from earthworm.d
756    ***********************************************/
757    lookup();
758
759
760    /* Set logit to LogSwitch read from configfile
761    *********************************************/
762    logit_init(argv[1], 0, 256, LogSwitch);
763    logit("", "gmewhtmlemail: Read command file <%s>\n", argv[1]);
764
765    logit("t", "gmewhtmlemail: version %s\n", VERSION_STR);
766
767    /* Get our own process ID for restart purposes
768    *********************************************/
769    if( (MyPid = getpid()) == -1 )
770    {
771        logit ("e", "gmewhtmlemail: Call to getpid failed. Exiting.\n");
772        free( GetLogo );
773        exit( -1 );
774    }
775
776    /* zero out the arc_list messages -
777    takes care of condition where Mag may
778    come in but no ARC (eqfiltered out)
779    ***********************************/
780    for (i=0; i< MAX_QUAKES; i++) arc_list[i] = NULL;
781
782    /* Allocate the message input buffer
783    ***********************************/
784    if ( !( msgbuf = (char *) calloc( 1, (size_t)MaxMessageSize+10 ) ) )
785    {
786        logit( "et",
787               "gmewhtmlemail: failed to allocate %ld bytes"
788               " for message buffer; exiting!\n", MaxMessageSize+10 );
789        free( GetLogo );
790        exit( -1 );
791    }
792
793    /* Initialize DB for quake ID translation
794     *****************************************/
795    if ( (db_path[0]==0) || (SQLITE_OK != (res = sqlite3_initialize())) )
796    {
797        if ( db_path[0] )
798            logit("et","Failed to initialize sqlite3 library: %d\n", res);
799        else
800            logit("t","sqlite3 library not specified\n");
801        dbAvailable = 0;
802    } else {
803        dbAvailable = 1;
804    }
805
806    /* Attach to shared memory rings
807    *******************************/
808    tport_attach( &InRegion, InRingKey );
809    logit( "", "gmewhtmlemail: Attached to public memory region: %ld\n",
810           InRingKey );
811
812
813    /* Force a heartbeat to be issued in first pass thru main loop
814    *************************************************************/
815    timeLastBeat = time(&timeNow) - HeartbeatInt - 1;
816
817
818    /* Flush the incoming transport ring on startup
819    **********************************************/
820    while( tport_copyfrom(&InRegion, GetLogo, nLogo,  &reclogo,
821                          &recsize, msgbuf, MaxMessageSize, &seq ) != GET_NONE )
822        ;
823
824
825    /*-------------------- setup done; start main loop ------------------------*/
826
827
828    while ( tport_getflag( &InRegion ) != TERMINATE  &&
829            tport_getflag( &InRegion ) != MyPid )
830    {
831        /* send heartbeat
832        ***************************/
833        if( HeartbeatInt  &&  time(&timeNow)-timeLastBeat >= HeartbeatInt )
834        {
835            timeLastBeat = timeNow;
836            status( TypeHeartBeat, 0, "" );
837        }
838
839        if ( next_delivery != -1 && timeNow >= next_delivery ) {
840            if ( Debug )
841                logit("","Process %s %ld\n", arc_list[next_arc]->fromArc ? "arc" : "trigger", arc_list[next_arc]->ha.sum.qid);
842            process_message( next_arc );
843            free_arcsm2( arc_list[next_arc] );
844            if ( arc_counter == 1 ) {
845                next_delivery = -1;
846                next_arc = -1;
847                arc_list[0] = NULL;
848            } else {
849                if ( next_arc != arc_counter-1 )
850                    arc_list[next_arc] = arc_list[arc_counter-1];
851                arc_list[arc_counter-1] = NULL;
852                next_delivery = arc_list[0]->deliveryTime;
853                next_arc = 0;
854                for ( i=1; i<arc_counter-1; i++ )
855                    if ( arc_list[i]->deliveryTime < next_delivery ) {
856                        next_delivery = arc_list[i]->deliveryTime;
857                        next_arc = i;
858                    }
859            }
860            arc_counter--;
861        }
862
863        /* Get msg & check the return code from transport
864        ************************************************/
865        res = tport_copyfrom( &InRegion, GetLogo, nLogo, &reclogo,
866                              &recsize, msgbuf, MaxMessageSize, &seq );
867        switch( res )
868        {
869        case GET_OK:      /* got a message, no errors or warnings         */
870            break;
871
872        case GET_NONE:    /* no messages of interest, check again later   */
873            sleep_ew(1000); /* milliseconds */
874            continue;
875
876        case GET_NOTRACK: /* got a msg, but can't tell if any were missed */
877            sprintf( Text,
878                     "Msg received (i%u m%u t%u); transport.h NTRACK_GET exceeded",
879                     reclogo.instid, reclogo.mod, reclogo.type );
880            status( TypeError, ERR_NOTRACK, Text );
881            break;
882
883        case GET_MISS_LAPPED:     /* got a msg, but also missed lots      */
884            sprintf( Text,
885                     "Missed msg(s) from logo (i%u m%u t%u)",
886                     reclogo.instid, reclogo.mod, reclogo.type );
887            status( TypeError, ERR_MISSLAP, Text );
888            break;
889
890        case GET_MISS_SEQGAP:     /* got a msg, but seq gap               */
891            sprintf( Text,
892                     "Saw sequence# gap for logo (i%u m%u t%u s%u)",
893                     reclogo.instid, reclogo.mod, reclogo.type, seq );
894            status( TypeError, ERR_MISSGAP, Text );
895            break;
896
897        case GET_TOOBIG:  /* next message was too big, resize buffer      */
898            sprintf( Text,
899                     "Retrieved msg[%ld] (i%u m%u t%u) too big for msgbuf[%ld]",
900                     recsize, reclogo.instid, reclogo.mod, reclogo.type,
901                     MaxMessageSize );
902            status( TypeError, ERR_TOOBIG, Text );
903            continue;
904
905        default:         /* Unknown result                                */
906            sprintf( Text, "Unknown tport_copyfrom result:%d", res );
907            status( TypeError, ERR_TOOBIG, Text );
908            continue;
909        }
910
911        /* Received a message. Start processing
912        **************************************/
913        msgbuf[recsize] = '\0'; /* Null terminate for ease of printing */
914
915        if (Debug) {
916            logit("t", "gmewhtmlemail: Received message, reclogo.type=%d\n",
917                  (int)(reclogo.type));
918        }
919
920        if ( reclogo.type == TypeHYP2000ARC )
921        {
922            time_t delivery_time;
923            if ( arc_counter == MAX_QUAKES-1 ) {
924                logit( "et", "Maximum number of events (%d) exceeded; ignoring latest\n", MAX_QUAKES );
925                continue;
926            }
927            arc = arc_data+arc_counter;
928            if ( Debug )
929                logit("t","Got a ARC msg: %s\n", msgbuf);
930            if ( arc_counter == MAX_QUAKES-1 ) {
931                logit( "et", "Maximum number of events (%d) exceeded; ignoring latest\n", MAX_QUAKES );
932            } else {
933                parse_arc_no_shdw(msgbuf, &(arc->ha));
934                time(&delivery_time);
935                if ( (MaxArcAge > 0) && (delivery_time - (arc->ha.sum.ot-GSEC1970) > MaxArcAge) ) {
936                    logit("t","Ignoring ARC %ld; too old\n", arc->ha.sum.qid );
937                    continue;
938                }
939                delivery_time += EmailDelay;
940                for ( i=0; i<arc_counter; i++ )
941                    if ( arc_list[i]->ha.sum.qid == arc->ha.sum.qid ) {
942                        if ( Debug )
943                            logit("","----Received duplicate ARC for %ld before original processed\n", arc->ha.sum.qid );
944                        arc_list[i]->deliveryTime = delivery_time;
945                        arc_list[i]->ha = arc->ha;
946                        break;
947                    }
948                if ( i==arc_counter )  {
949                    arc->deliveryTime = delivery_time;
950                    arc->sm_count = 0;
951                    arc_list[arc_counter] = arc;
952                    arc_counter++;
953                    if ( Debug )
954                        logit("","Added arc #%ld lat=%lf lon=%lf depth=%lf %d phases\n",
955                              arc->ha.sum.qid, arc->ha.sum.lat, arc->ha.sum.lon, arc->ha.sum.z, arc->ha.sum.nph);
956                    if ( arc_counter == 1 ) {
957                        next_delivery = delivery_time;
958                        next_arc = 0;
959                    }
960                    arc->fromArc = 1;
961                } else {
962                    if ( arc_counter > 1 ) {
963                        next_arc = 0;
964                        next_delivery = arc_list[0]->deliveryTime;
965                        for ( i=1; i<arc_counter; i++ )
966                            if ( arc_list[i]->deliveryTime < next_delivery ) {
967                                next_delivery = arc_list[i]->deliveryTime;
968                                next_arc = i;
969                            }
970                    }
971                    if ( Debug && next_delivery != delivery_time )
972                        logit( "", "----New head of queue: %ld\n", arc_list[next_arc]->ha.sum.qid );
973                }
974            }
975        } else if ( reclogo.type == TypeTHRESH )
976        {
977            if ( Debug )
978                logit("t","Got a THRESH message: %s\n", msgbuf );
979            if ( arc_counter == MAX_QUAKES-1 ) {
980                logit( "et", "Maximum number of events (%d) exceeded; ignoring latest\n", MAX_QUAKES );
981            } else {
982                arc = arc_data+arc_counter; //_calloc(1, sizeof(HypoArcSM2), "sm2 for thresh");
983                if ( arc == NULL ) {
984                    logit( "et", "Failed to allocated a HypoArcSM2\n" );
985                } else {
986                    int args, year, pos;
987                    struct tm when_tm;
988                    //time_t ot;
989                    char month_name[20];
990                    char sncl[4][MAX_SCNL_STRING];
991                    int j, k;
992
993                    //parse_arc_no_shdw(msgbuf, &(arc->ha));
994                    arc->ha.sum.qid = 0;
995                    args = sscanf( msgbuf, "SNCL=%s Thresh=%*f Value=%*f Time=%*s %s %d %d:%d:%d %d", 
996                                    arc->sncl, month_name, &when_tm.tm_mday, &when_tm.tm_hour, &when_tm.tm_min, 
997                                    &when_tm.tm_sec, &year );
998                    if ( args != 7 ) {
999                        logit("et", "Could not parse THRESH message (%s); ignoring\n", msgbuf );
1000                        continue;
1001                    }
1002                    for ( i=0; i<12; i++ )
1003                        if ( strcmp( monthNames[i], month_name ) == 0 ) {
1004                            when_tm.tm_mon = i;
1005                            break;
1006                        }
1007                    when_tm.tm_year = year - 1900;
1008
1009                    //arc->ha.sum.ot = timegm_ew( &when_tm );
1010                    arc->ha.sum.ot = timegm( &when_tm );
1011                    arc->ha.sum.qid = -arc->ha.sum.ot;
1012                    arc->ha.sum.ot += GSEC1970;
1013
1014                    j = 0;
1015                                        k = 0;
1016                    for ( i=0; arc->sncl[i]; i++ )
1017                        if ( arc->sncl[i] == '.'  ) {
1018                            sncl[j][k] = 0;
1019                            j++;
1020                            k = 0;
1021                        } else
1022                            sncl[j][k++] = arc->sncl[i];
1023                    sncl[j][k] = 0;
1024                    pos = site_index( sncl[0], sncl[1], sncl[2], sncl[3][0] ? sncl[3] : "--") ;
1025                    if ( pos != -1 ) {
1026                        arc->ha.sum.lat = Site[pos].lat;
1027                        arc->ha.sum.lon = Site[pos].lon;
1028                    } else {
1029                        arc->ha.sum.lat = arc->ha.sum.lon = -1;
1030                    }
1031                    arc->sm_count = 0;
1032                    time(&(arc->deliveryTime));
1033                    arc->deliveryTime += +EmailDelay;
1034                    arc_list[arc_counter] = arc;
1035                    arc_counter++;
1036                    if ( Debug )
1037                        logit("","Added thresh #%ld %s (%lf)\n", arc->ha.sum.qid, arc->sncl, arc->ha.sum.ot);
1038                    if ( arc_counter == 1 ) {
1039                        next_delivery = arc->deliveryTime;
1040                        next_arc = 0;
1041                    }
1042                    arc->fromArc = 0;
1043                }
1044            }
1045        } else if ( reclogo.type == TypeSTRONGMOTIONII )
1046        {
1047            char *ptr = msgbuf, gotOne = 0;
1048
1049            while ( 1 ) {
1050                SM_INFO sm;
1051                long n_qid;
1052                char sncl[MAX_SCNL_STRING];
1053                int rv = rd_strongmotionII( &ptr, &sm, 1 );
1054                if ( rv != 1 ) {
1055                    if ( !gotOne )
1056                        logit( "et", "Could not parse SM message; ignoring\n" );
1057                    break;
1058                }
1059                gotOne = 1;
1060                n_qid = atol( sm.qid );
1061                sprintf( sncl, "%s.%s.%s.%s", sm.sta, sm.net, sm.comp, sm.loc );
1062                for ( i=0; i<arc_counter; i++ )
1063                    if ( arc_list[i]->ha.sum.qid == n_qid )
1064                        break;
1065                if ( (i < arc_counter) && !arc_list[i]->fromArc ) {
1066                    if ( strcmp( sncl, arc_list[i]->sncl ) ) {
1067                        if ( Debug )
1068                            logit( "", "Ignoring SM from non-triggering station: %s\n", sncl);
1069                        continue;
1070                    }
1071                }
1072                if ( i < arc_counter ) {
1073                    int n = arc_list[i]->sm_count;
1074                    if ( n>=MAX_SM_PER_EVENT ) {
1075                        logit( "et", "Failed to expand sm_arr\n" );
1076                    } else {
1077                        int j;
1078                        for ( j=0; j<n; j++ )
1079                            if ( !(strcmp( arc_list[i]->sm_arr[j].sta, sm.sta ) ||
1080                                    strcmp( arc_list[i]->sm_arr[j].net, sm.net ) ||
1081                                    strcmp( arc_list[i]->sm_arr[j].comp, sm.comp ) ||
1082                                    strcmp( arc_list[i]->sm_arr[j].loc, sm.loc )) )
1083                                break;
1084                                                if((!RestrictThresholds) || (RestrictThresholds && (strcmp(RestrictedNetwork, sm.net) == 0))) {
1085                                arc_list[i]->sm_arr[j] = sm;
1086                                if ( j==n ) {
1087                                arc_list[i]->sm_count++;
1088                                if (Debug)
1089                                        logit("t","Added sm2 %s to %s (#%d) pga=%lf pgv=%lf pgd=%lf %d rsa\n",
1090                                        sncl, sm.qid, n+1, sm.pga, sm.pgv, sm.pgv, sm.nrsa);
1091                                }
1092                                else if ( Debug )
1093                                logit("t","Replaced sm2 %s for %s (#%d) pga=%lf pgv=%lf pgd=%lf %d rsa\n",
1094                                        sncl, sm.qid, n+1, sm.pga, sm.pgv, sm.pgv, sm.nrsa);
1095                                                }
1096                    }
1097                } else {
1098                    logit( "", "sm2 %ld w/ no matching trigger:", n_qid );
1099                    for ( i=0; i<arc_counter; i++ )
1100                        logit( "", "%ld ", arc_list[i]->ha.sum.qid );
1101                    logit( "", "\n" );
1102                }
1103            }
1104        } else {
1105            logit("","Unexpected message type: %d\n", reclogo.type );
1106        }
1107
1108    }
1109
1110    /* free allocated memory */
1111    free( GetLogo );
1112    free( msgbuf );
1113    free( Site );    // allocated by calling read_site()
1114
1115    /* detach from shared memory */
1116    tport_detach( &InRegion );
1117
1118    /* Shut down sqlite3, if in use */
1119    if ( dbAvailable )
1120        sqlite3_shutdown();
1121
1122    /* write a termination msg to log file */
1123    logit( "t", "gmewhtmlemail: Termination requested; exiting!\n" );
1124    fflush( stdout );
1125
1126    return( 0 );
1127}
1128
1129/*****************************************************************************
1130 *  config() processes command file(s) using kom.c functions;                *
1131 *                    exits if any errors are encountered.                   *
1132 *****************************************************************************/
1133#define ncommand 11        /* # of required commands you expect to process */
1134void config(char *configfile) {
1135        char init[ncommand]; /* init flags, one byte for each required command */
1136    int nmiss; /* number of required commands that were missed   */
1137    char *com;
1138    char *str;
1139    int nfiles;
1140    int success;
1141    char processor[15];
1142    int i;
1143    char cp[80];
1144    char path[200];
1145    int cfgNumLogo;
1146
1147    BlatOptions[0]=0;
1148    KMLdir[0]=0;
1149    strcpy(SubjectPrefix, DEFAULT_SUBJECT_PREFIX);
1150    strcpy(StaticMapType, DEFAULT_GOOGLE_STATIC_MAPTYPE);
1151    strcpy(MQStaticMapType, DEFAULT_MAPQUEST_STATIC_MAPTYPE);
1152
1153    /* Set to zero one init flag for each required command
1154    *****************************************************/
1155    for (i = 0; i < ncommand; i++)
1156        init[i] = 0;
1157    nLogo = 0;
1158
1159    /* Open the main configuration file
1160    **********************************/
1161    nfiles = k_open(configfile);
1162    if (nfiles == 0)
1163    {
1164        logit("e",
1165              "gmewhtmlemail: Error opening command file <%s>; exiting!\n",
1166              configfile);
1167        exit(-1);
1168    }
1169
1170    /* Process all command files
1171    ***************************/
1172        while (nfiles > 0) /* While there are command files open */
1173    {
1174        while (k_rd()) /* Read next line from active file  */
1175        {
1176            com = k_str(); /* Get the first token from line */
1177
1178            /* Ignore blank lines & comments
1179             *******************************/
1180            if (!com) continue;
1181            if (com[0] == '#') continue;
1182
1183            /* Open a nested configuration file
1184             **********************************/
1185            if (com[0] == '@') {
1186                success = nfiles + 1;
1187                nfiles = k_open(&com[1]);
1188                if (nfiles != success)
1189                {
1190                    logit("e",
1191                          "gmewhtmlemail: Error opening command file <%s>; exiting!\n",
1192                          &com[1]);
1193                    exit(-1);
1194                }
1195                continue;
1196            }
1197
1198            /* Process anything else as a command
1199             ************************************/
1200            /*0*/ if (k_its("LogFile"))
1201            {
1202                LogSwitch = k_int();
1203                if (LogSwitch < 0 || LogSwitch > 2)
1204                {
1205                    logit("e",
1206                          "gmewhtmlemail: Invalid <LogFile> value %d; "
1207                          "must = 0, 1 or 2; exiting!\n", LogSwitch);
1208                    exit(-1);
1209                }
1210                init[0] = 1;
1211            }
1212            /*1*/ else if (k_its("MyModuleId"))
1213            {
1214                if ((str = k_str()) != NULL) {
1215                    if (GetModId(str, &MyModId) != 0)
1216                    {
1217                        logit("e",
1218                              "gmewhtmlemail: Invalid module name <%s> "
1219                              "in <MyModuleId> command; exiting!\n", str);
1220                        exit(-1);
1221                    }
1222                }
1223                init[1] = 1;
1224            }
1225            /*2*/ else if (k_its("InRing"))
1226            {
1227                if ( (str = k_str()) != NULL)
1228                {
1229                    if ((InRingKey = GetKey(str)) == -1)
1230                    {
1231                        logit("e",
1232                              "gmewhtmlemail: Invalid ring name <%s> "
1233                              "in <InRing> command; exiting!\n", str);
1234                        exit(-1);
1235                    }
1236                }
1237                init[2] = 1;
1238            }
1239            /*3*/ else if (k_its("HeartbeatInt"))
1240            {
1241                HeartbeatInt = k_long();
1242                init[3] = 1;
1243            }
1244            /*4*/ else if (k_its("GetLogo"))
1245            {
1246                if ((str = k_str()) != NULL)
1247                {
1248                    MSG_LOGO *tlogo = NULL;
1249                    cfgNumLogo = nLogo + 10;
1250                    tlogo = (MSG_LOGO *)
1251                            realloc(GetLogo, cfgNumLogo * sizeof (MSG_LOGO) );
1252                    if (tlogo == NULL)
1253                    {
1254                        logit("e", "gmewhtmlemail: GetLogo: error reallocing"
1255                              " %zu bytes; exiting!\n",
1256                              cfgNumLogo * sizeof (MSG_LOGO));
1257                        exit(-1);
1258                    }
1259
1260                    GetLogo = tlogo;
1261
1262                    if (GetInst(str, &GetLogo[nLogo].instid) != 0)
1263                    {
1264                        logit("e",
1265                              "gmewhtmlemail: Invalid installation name <%s>"
1266                              " in <GetLogo> cmd; exiting!\n", str);
1267                        exit(-1);
1268                    }
1269                    GetLogo[nLogo + 3].instid = GetLogo[nLogo + 2].instid = GetLogo[nLogo + 1].instid = GetLogo[nLogo].instid;
1270                    if ((str = k_str()) != NULL)
1271                    {
1272                        if (GetModId(str, &GetLogo[nLogo].mod) != 0)
1273                        {
1274                            logit("e",
1275                                  "gmewhtmlemail: Invalid module name <%s>"
1276                                  " in <GetLogo> cmd; exiting!\n", str);
1277                            exit(-1);
1278                        }
1279                        GetLogo[nLogo + 3].mod = GetLogo[nLogo + 2].mod = GetLogo[nLogo + 1].mod = GetLogo[nLogo].mod;
1280                        if (GetType("TYPE_HYP2000ARC", &GetLogo[nLogo++].type) != 0)
1281                        {
1282                            logit("e",
1283                                  "gmewhtmlemail: Invalid message type <TYPE_HYP2000ARC>"
1284                                  "; exiting!\n");
1285                            exit(-1);
1286                        }
1287                        if (GetType("TYPE_STRONGMOTIONII", &GetLogo[nLogo++].type) != 0)
1288                        {
1289                            logit("e",
1290                                  "gmewhtmlemail: Invalid message type <TYPE_HYP2000ARC>"
1291                                  "; exiting!\n");
1292                            exit(-1);
1293                        }
1294                        if (GetType("TYPE_THRESH_ALERT", &GetLogo[nLogo++].type) != 0)
1295                        {
1296                            logit("e",
1297                                  "gmewhtmlemail: Invalid message type <TYPE_THRESH_ALERT>"
1298                                  "; exiting!\n");
1299                            exit(-1);
1300                        }
1301                    }
1302                    else
1303                    {
1304                        logit("e", "gmewhtmlemail: No module name "
1305                              "in <GetLogo> cmd; exiting\n");
1306                        exit(-1);
1307                    }
1308                }
1309                else
1310                {
1311                    logit("e", "gmewhtmlemail: No installation name "
1312                          "in <GetLogo> cmd; exiting\n");
1313                    exit(-1);
1314                }
1315                init[4] = 1;
1316            }
1317            /*5*/ else if (k_its("Debug"))
1318            {
1319                Debug = k_int();
1320                init[5] = 1;
1321            }
1322            else if (k_its("DebugEmail")) DebugEmail = k_int();
1323            else if ( k_its("MaxMessageSize") )
1324            {
1325                MaxMessageSize = k_long();
1326            }
1327            else if ( k_its("MAX_SAMPLES") )
1328            {
1329                MAX_SAMPLES = k_int();
1330            }
1331            else if ( k_its("MinQuality") )
1332            {
1333                str = k_str();
1334                MinQuality = str[0];
1335            }
1336            else if ( k_its("WSTimeout") )
1337            {
1338                wstimeout = k_int() * 1000;
1339            }
1340            /*6*/ else if ( k_its("HTMLFile") )
1341            {
1342                strncpy(HTMLFile, k_str(), MAX_STRING_SIZE);
1343                init[6] = 1;
1344            }
1345            else if ( k_its("EmailProgram") )
1346            {
1347                strncpy(EmailProgram, k_str(), MAX_STRING_SIZE);
1348            }
1349            else if ( k_its("KML") )
1350            {
1351                strncpy(KMLdir, k_str(), MAX_STRING_SIZE);
1352                strncpy(KMLpreamble, k_str(), MAX_STRING_SIZE);
1353            }
1354            else if ( k_its("StyleFile") )
1355            {
1356                /* RSL: Removed styles */
1357                //strcpy(StyleFile, k_str());
1358            }
1359            else if ( k_its("SubjectPrefix") )
1360            {
1361                strcpy(SubjectPrefix, k_str());
1362            }
1363            else if ( k_its("NoWaveformPlots") )
1364            {
1365                                NoWaveformPlots = 1;
1366            }
1367            else if ( k_its("TimeMargin") )
1368            {
1369                TimeMargin = k_val();
1370            }
1371            else if ( k_its("WaveServer") )
1372            {
1373                if (nwaveservers < MAX_WAVE_SERVERS)
1374                {
1375                    strcpy(cp,k_str());
1376                    strcpy(waveservers[nwaveservers].wsIP, strtok (cp, ":"));
1377                    strcpy(waveservers[nwaveservers].port, strtok (NULL, ":"));
1378                    nwaveservers++;
1379                }
1380                else
1381                {
1382                    logit("e", "gmewhtmlemail: Excessive number of waveservers. Exiting.\n");
1383                    exit(-1);
1384                }
1385            }
1386            else if ( k_its("UseRegionName") )
1387            {
1388                UseRegionName = 1;
1389            }
1390            else if ( k_its("UseBlat") )
1391            {
1392                UseBlat = 1;
1393            }
1394            else if ( k_its("BlatOptions") )
1395            {
1396                strcpy(BlatOptions, k_str());
1397            }
1398            else if ( k_its("StaticMapType") )
1399            {
1400                strcpy(StaticMapType, k_str());
1401                if ( strcmp(StaticMapType,"hybrid")==0)
1402                    strcpy(MQStaticMapType,"hyb");
1403                else if (strcmp(StaticMapType,"terrain")==0||strcmp(StaticMapType,"roadmap")==0)
1404                    strcpy(MQStaticMapType,"map");
1405                else if (strcmp(StaticMapType,"satellite")==0)
1406                    strcpy(MQStaticMapType,"sat");
1407            }
1408            else if ( k_its("DontShowMd") )
1409            {
1410                DontShowMd = 1;
1411            }
1412            else if ( k_its("DontUseMd") )
1413            {
1414                DontUseMd = 1;
1415            }
1416            else if ( k_its("UseUTC") )
1417            {
1418                UseUTC = 1;
1419            }
1420            else if ( k_its("MaxDuration") )
1421            {
1422                DurationMax = k_val();
1423            }
1424            else if ( k_its("EmailDelay") )
1425            {
1426                EmailDelay = k_val();
1427            }
1428            else if ( k_its("CenterPoint") )
1429            {
1430                center_lat = k_val();
1431                center_lon = k_val();
1432            }
1433            else if ( k_its("DataCenter") )
1434            {
1435                strcpy(DataCenter, strtok(k_str(),"\""));
1436            }
1437            /*   else if (k_its("SPfilter"))
1438                     {
1439                        SPfilter = 1;
1440                     } */
1441            else if (k_its("GMTmap"))
1442            {
1443                GMTmap = 1;
1444            }
1445            else if (k_its("Cities"))
1446            {
1447                strncpy(Cities, k_str(), MAX_STRING_SIZE);
1448            }
1449            else if (k_its("StationNames"))
1450            {
1451                StationNames = 1;
1452            }
1453            else if (k_its("MapLegend"))
1454            {
1455                strcpy(MapLegend, k_str());
1456            }
1457            else if ( k_its("Mercator") )
1458            {
1459                Mercator = 1;
1460            }
1461            else if ( k_its("Albers") )
1462            {
1463                Albers=1;
1464                Mercator=0;
1465            }
1466            else if ( k_its("ShowDetail") )
1467            {
1468                ShowDetail = 1;
1469            }
1470            else if ( k_its("ShortHeader") )
1471            {
1472                ShortHeader = 1;
1473            }
1474                        else if ( k_its("EwaveAddr") )
1475                        {
1476                                if ((str = k_str()) != NULL) {
1477                                        strncpy(ewaveAddr, str, 64);
1478                                        UseEWAVE = 1;
1479                                }
1480                        }
1481                        else if ( k_its("EwavePort") )
1482                        {
1483                                if ((str = k_str()) != NULL) {
1484                                        strncpy(ewavePort, str, 16);
1485                                }
1486                        }
1487                        else if ( k_its("OnlyAllowThresholdsFromNetwork") )
1488                        {
1489                                if ((str = k_str()) != NULL) {
1490                                        strncpy(RestrictedNetwork, str, 8);
1491                                        RestrictThresholds = 1;
1492                                }
1493                        }
1494            else if ( k_its("UseGIF") )
1495            {
1496                UseGIF = 1;
1497            }
1498            else if ( k_its("TraceWidth") )
1499            {
1500                TraceWidth = (int) k_int();
1501            }
1502            else if ( k_its("TraceWidth") )
1503            {
1504                TraceWidth = (int) k_int();
1505            }
1506            /*7*/ else if ( k_its("site_file") )
1507            {
1508                strcpy(path, k_str());
1509                site_read ( path );
1510                init[7] = 1;
1511            }
1512            /*8*/ else if ( k_its("dam_file") )
1513            {
1514                logit("e","gmewhtmlemail: dam_file has been deprecated; use RAD_file.\n" );
1515            }
1516            /*8*/ else if ( k_its("full_dam_file") )
1517            {
1518                logit("e","gmewhtmlemail: full_dam_file has been deprecated; use RAD_file.\n" );
1519            }
1520            /*8*/ else if ( k_its("RAD_file") )
1521            {
1522                strcpy(path, k_str());
1523                read_RAD_info ( path );
1524                init[8] = 1;
1525            }
1526            /*9*/ else if ( k_its("db_path") )
1527            {
1528                strncpy(db_path, k_str(), MAX_STRING_SIZE);
1529                init[9] = 1;
1530            }
1531                        else if ( k_its("conversions_file") )
1532                        {
1533                                strcpy(path, k_str());
1534                                if(conversion_read ( path ) == -1) {
1535                                        logit("et", "gmewhtmlemail: failed to process conversion factor file\n");
1536                                }
1537                        }
1538            else if ( k_its("MaxStationDist") )
1539            {
1540                MaxStationDist = (int) k_int();
1541            }
1542            else if ( k_its("MaxFacilityDist") )
1543            {
1544                MaxFacilityDist = (int) k_int();
1545            }
1546            else if ( k_its("IgnoreArcOlderThanSeconds") )
1547            {
1548                MaxArcAge = (int) k_int();
1549            }
1550            else if ( k_its("EmailRecipientWithMinMagAndDist") )
1551            {
1552                if ( makeRoomForRecipients(1) )
1553                {
1554                    emailrecipients[nemailrecipients-1].subscription = 0;
1555                    strcpy(emailrecipients[nemailrecipients-1].address, k_str());
1556                    emailrecipients[nemailrecipients-1].min_magnitude = k_val();
1557                    emailrecipients[nemailrecipients-1].max_distance = k_val();
1558                    str = assignMailingList();
1559                    if ( str )  {
1560                        logit("e","gmewhtmlemail: Illegal EmailRecipientWithMinMagAndDist mailing list modifier: '%s'\n", str );
1561                        exit(-1);
1562                    }
1563                    nStaticEmailRecipents++;
1564                }
1565                else
1566                {
1567                    logit("e", "gmewhtmlemail: Failed to allocate space for EmailRecipientWithMinMagAndDist; exiting.\n");
1568                    exit(-1);
1569                }
1570            }
1571            else if ( k_its("EmailRecipientWithMinMag") )
1572            {
1573                if ( makeRoomForRecipients(1) )
1574                {
1575                    emailrecipients[nemailrecipients-1].subscription = 0;
1576                    strcpy(emailrecipients[nemailrecipients-1].address, k_str());
1577                    emailrecipients[nemailrecipients-1].min_magnitude = k_val();
1578                    emailrecipients[nemailrecipients-1].max_distance  = OTHER_WORLD_DISTANCE_KM;
1579                    str = assignMailingList();
1580                    if ( str )  {
1581                        logit("e","gmewhtmlemail: Illegal EmailRecipientWithMinMag mailing list modifier: '%s'\n", str );
1582                        exit(-1);
1583                    }
1584                    nStaticEmailRecipents++;
1585                }
1586                else
1587                {
1588                    logit("e", "gmewhtmlemail: Failed to allocate space for EmailRecipientWithMinMag; exiting.\n");
1589                    exit(-1);
1590                }
1591            }
1592            else if ( k_its("EmailRecipient") )
1593            {
1594                if ( makeRoomForRecipients(1) )
1595                {
1596                    emailrecipients[nemailrecipients-1].subscription = 0;
1597                    strcpy(emailrecipients[nemailrecipients-1].address, k_str());
1598                    emailrecipients[nemailrecipients-1].min_magnitude = DUMMY_MAG;
1599                    emailrecipients[nemailrecipients-1].max_distance  = OTHER_WORLD_DISTANCE_KM;
1600                    str = assignMailingList();
1601                    if ( str )  {
1602                        logit("e","gmewhtmlemail: Illegal EmailRecipient mailing list modifier: '%s'\n", str );
1603                        exit(-1);
1604                    }
1605                    nStaticEmailRecipents++;
1606                }
1607                else
1608                {
1609                    logit("e", "gmewhtmlemail: Failed to allocate space for EmailRecipient; exiting.\n");
1610                    exit(-1);
1611                }
1612            }
1613            else if ( k_its("ShowMiles") )
1614            {
1615                ShowMiles = 1;
1616            }
1617            else if ( k_its("PlotAllSCNLs") )
1618            {
1619                PlotAllSCNLs = 1;
1620            }
1621            else if ( k_its("EarthquakeDisclaimer") )
1622            {
1623                strcpy(path, k_str());
1624                EarthquakeDisclaimer = read_disclaimer ( path );
1625            }
1626            else if ( k_its("TriggerDisclaimer") )
1627            {
1628                strcpy(path, k_str());
1629                TriggerDisclaimer = read_disclaimer ( path );
1630            }
1631            else if ( k_its("MapQuestKey") )
1632            {
1633                strcpy(MapQuestKey, k_str());
1634            }
1635            else if ( k_its("ShowRegionAndArea") )
1636            {
1637                ShowRegionAndArea = 1;
1638                facility_font_size = 1;
1639            }
1640            else if ( k_its("IgnoreOlderThanHours") )
1641            {
1642                IgnoreHours = k_val();
1643            }
1644            else if ( k_its("PathToPython") )
1645            {
1646                strncpy(PathToPython, k_str(), MAX_STRING_SIZE);
1647            }
1648            else if ( k_its("PathToLocaltime") )
1649            {
1650                strncpy(PathToLocaltime, k_str(), MAX_STRING_SIZE);
1651            }
1652            /* 10 */
1653            else if ( k_its("PathToEventEmail") )
1654            {
1655                strncpy(PathToEventEmail, k_str(), MAX_STRING_SIZE);
1656                init[10] = 1;
1657            }
1658            else if ( k_its("ReportEstimatedPGAs") )
1659            {
1660                ReportEstimatedPGAs = 1;
1661                strcpy( EstimatePGAsPath, k_str() );
1662            }
1663
1664            else if ( k_its("SubscriptionPath") )
1665            {
1666                strcpy( SubscriptionPath, k_str() );
1667            }
1668
1669            else if ( k_its("DontHiliteMax") )
1670            {
1671                showMax = 0;
1672            }
1673            else if ( k_its("MaxFacilitiesOnMap") )
1674            {
1675                damMapLimit = k_int();
1676            }
1677            else if ( k_its("MinPGA") )
1678            {
1679                PGAThreshold = k_val();
1680            }
1681            else if ( k_its("MagDist") )
1682            {
1683                if (NMagDist == NMagDistAlloc) {
1684                        logit("e", "gmewhtmlemail: too many MagDist entries, reduce to less than %d. Exiting.\n",
1685                              NMagDistAlloc);
1686                        exit(-1);
1687                }
1688                MagDist[NMagDist].mag = k_val();
1689                MagDist[NMagDist].dist = k_val();
1690                NMagDist++;
1691            }
1692            else if ( k_its("IgnorePGABelow") )
1693            {
1694                IgnorePGABelow = k_val();
1695            }
1696            else if ( k_its("MaxFacilitiesInTable") )
1697            {
1698                MaxFacilitiesInTable = k_int();
1699            }
1700            else if ( k_its("ColorPGA") )
1701            {
1702                if (ncolorpga < MAX_COLOR_PGAS)
1703                {
1704                    colorpga_values[ncolorpga] = k_val();
1705                    // if other color PGA values and this value is not less than previous value
1706                    if (ncolorpga > 0 && colorpga_values[ncolorpga] >= colorpga_values[ncolorpga - 1])
1707                    {
1708                        logit("e", "gmewhtmlemail: Invalid ColorPGA value (%lf). Exiting.\n",
1709                              colorpga_values[ncolorpga]);
1710                        exit(-1);
1711                    }
1712                    strncpy(colorpga_colors[ncolorpga], k_str(), MAX_STRING_SIZE);
1713                    ncolorpga++;
1714                }
1715                else
1716                {
1717                    logit("e", "gmewhtmlemail: Excessive number of ColorPGA values. Exiting.\n");
1718                    exit(-1);
1719                }
1720            }
1721
1722           
1723
1724            /* Some commands may be processed by other functions
1725             ***************************************************/
1726            else if( site_com() )  strcpy( processor, "site_com" );
1727            /* Unknown command
1728             *****************/
1729            else
1730            {
1731                logit("e", "gmewhtmlemail: <%s> Unknown command in <%s>.\n",
1732                      com, configfile);
1733                continue;
1734            }
1735
1736            /* See if there were any errors processing the command
1737             *****************************************************/
1738            if (k_err())
1739            {
1740                logit("e",
1741                      "gmewhtmlemail: Bad <%s> command in <%s>; exiting!\n",
1742                      com, configfile);
1743                exit(-1);
1744            }
1745        }
1746        nfiles = k_close();
1747    }
1748
1749    /* After all files are closed, check init flags for missed commands
1750    ******************************************************************/
1751    nmiss = 0;
1752    for (i = 0; i < ncommand; i++) if (!init[i]) nmiss++;
1753    if (nmiss)
1754    {
1755        logit("e", "gmewhtmlemail: ERROR, no ");
1756        if (!init[0]) logit("e", "<LogFile> ");
1757        if (!init[1]) logit("e", "<MyModuleId> ");
1758        if (!init[2]) logit("e", "<InRing> ");
1759        if (!init[3]) logit("e", "<HeartbeatInt> ");
1760        if (!init[4]) logit("e", "<GetLogo> ");
1761        if (!init[5]) logit("e", "<Debug> ");
1762        if (!init[6]) logit("e", "<HTMLFile> ");
1763        if (!init[7]) logit("e", "<site_file> ");
1764        if (!init[8]) logit("e", "<RAD_file> ");
1765        if (!init[9]) logit("e", "<db_path> ");
1766        if (!init[10]) logit("e", "<PathToEventEmail> ");
1767        logit("e", "command(s) in <%s>; exiting!\n", configfile);
1768        exit(-1);
1769    }
1770
1771    if ( SubscriptionPath[0] )
1772        updateSubscriptions();
1773       
1774    return;
1775}
1776
1777/*********************************************************************
1778 *  lookup( )   Look up important info from earthworm.h tables       *
1779 *********************************************************************/
1780void lookup(void) {
1781    /* Look up installations of interest
1782     *********************************/
1783    if (GetLocalInst(&InstId) != 0) {
1784        logit("e",
1785              "gmewhtmlemail: error getting local installation id; exiting!\n");
1786        exit(-1);
1787    }
1788
1789    /* Look up message types of interest
1790     *********************************/
1791    if (GetType("TYPE_HEARTBEAT", &TypeHeartBeat) != 0) {
1792        logit("e",
1793              "gmewhtmlemail: Invalid message type <TYPE_HEARTBEAT>; exiting!\n");
1794        exit(-1);
1795    }
1796    if (GetType("TYPE_ERROR", &TypeError) != 0) {
1797        logit("e",
1798              "gmewhtmlemail: Invalid message type <TYPE_ERROR>; exiting!\n");
1799        exit(-1);
1800    }
1801    if (GetType("TYPE_HYP2000ARC", &TypeHYP2000ARC) != 0) {
1802        logit("e",
1803              "gmewhtmlemail: Invalid message type <TYPE_HYP2000ARC>; exiting!\n");
1804        exit(-1);
1805    }
1806    if (GetType("TYPE_TRACEBUF2", &TypeTraceBuf2) != 0) {
1807        logit("e",
1808              "gmewhtmlemail: Invalid message type <TYPE_TRACEBUF2>; exiting!\n");
1809        exit(-1);
1810    }
1811    if (GetType("TYPE_STRONGMOTIONII", &TypeSTRONGMOTIONII) != 0) {
1812        logit("e",
1813              "gmewhtmlemail: Invalid message type <TYPE_STRONGMOTIONII>; exiting!\n");
1814        exit(-1);
1815    }
1816    if (GetType("TYPE_THRESH_ALERT", &TypeTHRESH) != 0) {
1817        logit("e",
1818              "gmewhtmlemail: Invalid message type <TYPE_THRESH_ALERT>; exiting!\n");
1819        exit(-1);
1820    } else
1821        logit( "t", "gmewhtmlemail: TYPE_THRESH_ALERT = %d\n", TypeTHRESH );
1822    return;
1823}
1824
1825/******************************************************************************
1826 * status() builds a heartbeat or error message & puts it into                *
1827 *                   shared memory.  Writes errors to log file & screen.      *
1828 ******************************************************************************/
1829void status(unsigned char type, short ierr, char *note) {
1830    MSG_LOGO logo;
1831    char msg[256];
1832    long size;
1833    time_t t;
1834
1835    /* Build the message
1836     *******************/
1837    logo.instid = InstId;
1838    logo.mod = MyModId;
1839    logo.type = type;
1840
1841    time(&t);
1842
1843    if (type == TypeHeartBeat) {
1844        sprintf(msg, "%ld %ld\n", (long) t, (long) MyPid);
1845    } else if (type == TypeError) {
1846        sprintf(msg, "%ld %hd %s\n", (long) t, ierr, note);
1847        logit("et", "gmewhtmlemail: %s\n", note);
1848    }
1849
1850    size = strlen(msg); /* don't include the null byte in the message */
1851
1852    /* Write the message to shared memory
1853     ************************************/
1854    if (tport_putmsg(&InRegion, &logo, size, msg) != PUT_OK) {
1855        if (type == TypeHeartBeat) {
1856            logit("et", "gmewhtmlemail:  Error sending heartbeat.\n");
1857        } else if (type == TypeError) {
1858            logit("et", "gmewhtmlemail:  Error sending error:%d.\n", ierr);
1859        }
1860    }
1861
1862    return;
1863}
1864
1865
1866/******************************************************************************
1867 * formatTimestamps() builds (up to) 2 strings representing the time in ot;   *
1868 *          one in UTC, the other in the local time for the location at       *
1869 *          (lat,lon) (if timestr isn't NULL).  Returns 0 if successful       *
1870 ******************************************************************************/
1871int formatTimestamps( time_t ot, double lat, double lon, char* timestr, char* timestrUTC ) {
1872    struct tm   mytimeinfo;
1873    char        cmdStr[512];
1874    FILE *fp;
1875
1876    if ( timestrUTC==NULL ) {
1877        logit("e","formatTimestamps: timestr == NULL\n");
1878        return 3;
1879    }
1880
1881    gmtime_ew ( &ot, &mytimeinfo );
1882    strftime( timestrUTC, 80, "%Y.%m.%d %H:%M:%S", &mytimeinfo ); // Prepare origin time (UTC)
1883
1884    if ( timestr==NULL )
1885        return 0;
1886
1887    snprintf( cmdStr, 512, "%s %s %lf %lf %s", PathToPython, PathToLocaltime, lon, lat, timestrUTC );
1888    if ( Debug )
1889        logit( "", "Local time command: %s\n", cmdStr );
1890    fp = popen( cmdStr, "r" );
1891    if ( fp == NULL) {
1892        logit("e","formatTimestamps: popen failed\n");
1893        return 1;
1894    }
1895    if ( fgets( timestr, 80, fp ) == NULL ) {
1896        logit("e","formatTimestamps: failed to read from popen\n");
1897        return 2;
1898    }
1899    pclose( fp );
1900    return 0;
1901}
1902
1903
1904/******************************************************************************
1905 * EstimatePGA() yields the estimated PGA for (lat,lon) of an event at        *
1906 *          (evt_lat,evt_lon) at depth evt_depth and magnitude evt_mag.       *
1907 *          Yields a negative number if an error occurs.                      *
1908 *    PGA from Java utility returned in units of g, 1g = 980cm/s/s/
1909 ******************************************************************************/
1910double EstimatePGA( double lat, double lon, double evt_lat, double evt_lon, double evt_depth, double evt_mag )  {
1911    char        cmdStr[512], resultStr[64];
1912    double ePGA;
1913    FILE *fp;
1914
1915    snprintf( cmdStr, 512,  "java -cp %s/GmpeGmm.jar -DPGACALC_NO_RESULT=OOPS com.isti.gmpegmm.DeterministicSpectra \"Dam\" %lf %lf %lf %lf %lf %lf",
1916             EstimatePGAsPath, lon, lat, evt_mag, evt_lon, evt_lat, evt_depth );
1917    if ( Debug )
1918        logit( "", "ePGA call: %s\n", cmdStr );
1919
1920    fp = popen( cmdStr, "r" );
1921    if ( fp == NULL ) {
1922        logit("e","EstimatePGA: popen failed\n");
1923        return -1;
1924    }
1925    if ( fgets( resultStr, 80, fp ) == NULL ) {
1926        logit("e","EstimatePGA: failed to read from popen\n");
1927        return -2;
1928    }
1929    pclose( fp );
1930
1931    if ( sscanf( resultStr, "%lf", &ePGA ) != 1 ) {
1932        logit("e","EstimatePGA: failed to parse GmpeGmm result (%s)\n", resultStr);
1933        return -3;
1934    }
1935    if ( Debug )
1936        logit("","EstimatePGA result: %lf (%s)\n", ePGA, resultStr );
1937    return ePGA;
1938}
1939
1940static char* determine_pga_color(double pga)
1941{
1942    if (ncolorpga > 0)
1943    {
1944        int i;
1945        for ( i = 0; i < ncolorpga; i++)
1946        {
1947            if (pga >= colorpga_values[i])
1948            {
1949                return colorpga_colors[i];
1950            }
1951        }
1952    }
1953    return NULL;
1954}
1955
1956static void print_table_header_cell(FILE *htmlfile, char *text)
1957{
1958    fprintf( htmlfile, "<th><font size=\"1\" face=\"Sans-serif\" color=\"FFFFFF\">%s</font></th>", text);
1959}
1960
1961static void print_table_cell(FILE *htmlfile, double value)
1962{
1963    fprintf( htmlfile, "<td style=\"text-align:right\"><font size=\"1\" face=\"Sans-serif\">%1.6lf</font></td>", value);
1964}
1965
1966static void print_table_cell_color(FILE *htmlfile, double value, char *color)
1967{
1968    if (color == NULL)
1969    {
1970        fprintf( htmlfile, "<td style=\"text-align:right\"><font size=\"1\" face=\"Sans-serif\">%1.6lf</font></td>", value);
1971    }
1972    else
1973    {
1974        fprintf( htmlfile, "<td style=\"text-align:right;background-color:%s\"><font size=\"1\" face=\"Sans-serif\">%1.6lf</font></td>", color, value);
1975    }
1976}
1977
1978/****************************************************************************
1979 * InsertStationTable(): adds station table to HTML email
1980 *                      calculates snl_order, the order streams will be displayed in
1981 *
1982 * @params:
1983 *
1984 * in: htmlfile, fromArc, nsites, site_order[], sorted_sites[], smForSite[]
1985 * in/out: snl_sites, snl_order[]
1986 *
1987 * returns 0 if successful, -1 otherwise
1988 * */
1989int InsertStationTable(FILE *htmlfile, char fromArc, int nsites,
1990                                                SiteOrder site_order[MAX_STATIONS], SITE *sorted_sites[MAX_STATIONS],
1991                                                SM_INFO *smForSite[MAX_STATIONS], int *snl_sites,
1992                                                SiteOrder snl_order[MAX_STATIONS], double distances[MAX_STATIONS])
1993{
1994        int i = 0, j = 0, ix = 0, jx = 0, snl_i = 0; //various counter variables
1995        int num_snl = 0;        //so we don't have to always deference snl_sites
1996        char *color;
1997        double pgapg;
1998
1999        //Calculate the appropriate stream order:
2000    *snl_sites = num_snl = 1;
2001    if ( (fromArc) && (nsites > 0) ) {
2002        logit("t", "Computing SNL order\n");
2003        snl_order[0] = site_order[0];
2004        for ( i=1; i<nsites; i++ ) {
2005                if ( PlotAllSCNLs )
2006                snl_order[num_snl++] = site_order[i];
2007            else {
2008                ix = site_order[i].idx;
2009                for ( j=0; j<num_snl; j++ ) {
2010                        jx = snl_order[j].idx;
2011                    if ( !strcmp( smForSite[ix]->sta, smForSite[jx]->sta) &&
2012                                !strcmp( smForSite[ix]->net, smForSite[jx]->net) &&
2013                                !strcmp( smForSite[ix]->loc, smForSite[jx]->loc) ) {
2014                        if ( smForSite[ix]->pga > smForSite[jx]->pga ) {
2015                                                        logit("t", "Moving %s.%s to head (%lf %lf)\n", 
2016                                                                        smForSite[ix]->sta, smForSite[ix]->comp, 
2017                                                                        smForSite[ix]->pga, smForSite[jx]->pga );
2018                                snl_order[j].idx = ix;
2019                        }
2020                        break;
2021                    }
2022                }
2023                if ( j == num_snl ) {
2024                    snl_order[num_snl] = site_order[i];
2025                    num_snl++;
2026                }
2027                }
2028        }
2029        } else {
2030        for ( i=0; i<nsites; i++ ) {
2031                snl_order[i] = site_order[i];
2032                }
2033                num_snl = nsites;
2034    }
2035
2036    if ( nsites > 0 ) {
2037        fprintf( htmlfile, "<table id=\"StationTable\" border=\"0\" cellspacing=\"1\" cellpadding=\"3\" width=\"600px\">\n" );
2038        fprintf( htmlfile, "<tr bgcolor=\"000060\">");
2039
2040        fprintf( htmlfile, "<th>#</th>");
2041        print_table_header_cell( htmlfile, "Station");
2042        if ( fromArc ) {
2043                print_table_header_cell( htmlfile, "Distance (km)");
2044            if ( ShowMiles )
2045                print_table_header_cell( htmlfile, "Distance (mi)");
2046        }
2047        print_table_header_cell( htmlfile, "AI");
2048        print_table_header_cell( htmlfile, "PGA (%g)");
2049        print_table_header_cell( htmlfile, "PGA (g)");
2050        print_table_header_cell( htmlfile, "PGA (cm/s/s)");
2051        print_table_header_cell( htmlfile, "PGV (cm/s)");
2052        print_table_header_cell( htmlfile, "PGD (cm)");
2053        print_table_header_cell( htmlfile, "RSA 0.3s (cm/s/s)");
2054        print_table_header_cell( htmlfile, "RSA 1s (cm/s/s)");
2055        print_table_header_cell( htmlfile, "RSA 3s (cm/s/s)");
2056        fprintf( htmlfile, "</tr>\n" );
2057
2058        snl_i = 0;
2059        for( i = 0; i < nsites; i++ )
2060        {
2061                ix = site_order[i].idx;
2062            fprintf( htmlfile,"<tr bgcolor=\"#%s\">", i%2==0 ? "DDDDFF" : "FFFFFF");
2063            if ( snl_order[snl_i].idx == ix ) {
2064                snl_i++;
2065                fprintf( htmlfile, "<th><font size=\"1\" face=\"Sans-serif\">%d</font></th>", snl_i );
2066            } else {
2067                fprintf( htmlfile, "<th><font size=\"1\" face=\"Sans-serif\"></font></th>" );
2068                        }
2069            fprintf( htmlfile, "<td><font size=\"1\" face=\"Sans-serif\">%s.%s.%s.%s</font></td>",
2070                     sorted_sites[i]->name, sorted_sites[i]->comp,
2071                     sorted_sites[i]->net, sorted_sites[i]->loc );
2072            if ( fromArc ) {
2073                fprintf( htmlfile, "<td style=\"text-align:right\"><font size=\"1\" face=\"Sans-serif\">%1.1lf</font></td>", distances[ix]);
2074                if ( ShowMiles ) {
2075                        fprintf( htmlfile, "<td style=\"text-align:right\"><font size=\"1\" face=\"Sans-serif\">%1.1lf</font></td>", distances[ix]*KM2MILES);
2076                }
2077                        }
2078            print_table_cell( htmlfile, smForSite[ix]->ai);
2079            pgapg = 100*smForSite[ix]->pga/CM_S_S_1_g;
2080            color = determine_pga_color(pgapg);
2081            print_table_cell_color( htmlfile, pgapg, color);
2082            print_table_cell_color( htmlfile, smForSite[ix]->pga/CM_S_S_1_g, color);
2083            print_table_cell_color( htmlfile, smForSite[ix]->pga, color);
2084            print_table_cell( htmlfile, smForSite[ix]->pgv);
2085            print_table_cell( htmlfile, smForSite[ix]->pgd);
2086            for ( j=0; j<3; j++ ) {
2087                if ( j < smForSite[i]->nrsa) {
2088                        print_table_cell( htmlfile, smForSite[ix]->rsa[j]);
2089                                } else {
2090                    fprintf( htmlfile, "<td>&nbsp;</td>");
2091                                }
2092                        }
2093            fprintf( htmlfile, "</tr>\n" );
2094        }
2095        fprintf( htmlfile, "</table><br/><br/>\n" );
2096    } else if ( fromArc ) {
2097        fprintf( htmlfile, "<p>No sites for table</p><br/><br/>");
2098    }
2099
2100        *snl_sites = num_snl;
2101        return 0;
2102}
2103
2104/*
2105 * InsertDamTable(): inserts HTML table element displaying dam info
2106 *              calculates an estimate of PGA at facility
2107 *
2108 * @params:
2109 * in- htmlfile, HypoArc *,
2110 *
2111 * in/out: double *max_epga_g
2112 *
2113 * returns 0 if successful, -1 otherwise
2114 * */
2115int InsertDamTable(FILE *htmlfile, HypoArc *arc, double *max_epga_g)
2116{
2117        char temp[MAX_GET_CHAR] = {0};
2118        double ePGA = 0.0;
2119        int damTblColumns = 3;
2120        int i = 0;
2121
2122        //Output dam table:
2123    fprintf( htmlfile, "<table id=\"DamTable\" border=\"0\" cellspacing=\"1\" cellpadding=\"3\" width=\"600px\">\n" );
2124    fprintf( htmlfile, "<tr bgcolor=\"000060\">");
2125    fprintf( htmlfile, "<th><font size=\"%d\" face=\"Sans-serif\" color=\"FFFFFF\">#</font></th>", facility_font_size);
2126    fprintf( htmlfile, "<th><font size=\"%d\" face=\"Sans-serif\" color=\"FFFFFF\">Facility</font></th>", facility_font_size);
2127    if ( ShowRegionAndArea ) {
2128        fprintf( htmlfile, "<th><font size=\"%d\" face=\"Sans-serif\" color=\"FFFFFF\">Region</font></th>", facility_font_size);
2129        fprintf( htmlfile, "<th><font size=\"%d\" face=\"Sans-serif\" color=\"FFFFFF\">Area</font></th>", facility_font_size);
2130    }
2131    fprintf( htmlfile, "<th><font size=\"%d\" face=\"Sans-serif\" color=\"FFFFFF\">Dist.(km)</font></th>", facility_font_size);
2132    if ( ShowMiles ) {
2133        fprintf( htmlfile, "<th><font size=\"%d\" face=\"Sans-serif\" color=\"FFFFFF\">Dist.(mi)</font></th>", facility_font_size);
2134        }
2135        if ( ReportEstimatedPGAs ) {
2136        fprintf( htmlfile, "<th><font size=\"%d\" face=\"Sans-serif\" color=\"FFFFFF\">PGA(est.)(%%g)</font></th>", facility_font_size);
2137        fprintf( htmlfile, "<th><font size=\"%d\" face=\"Sans-serif\" color=\"FFFFFF\">PGA(est.)(g)</font></th>", facility_font_size);
2138    }
2139    fprintf( htmlfile, "</tr>\n" );
2140
2141    damTblColumns = 3;
2142    if ( ShowRegionAndArea )    damTblColumns += 2;
2143    if ( ShowMiles )            damTblColumns += 1;
2144    if ( ReportEstimatedPGAs )  damTblColumns += 2;
2145
2146    for ( i=0; i<dam_close_count; i++ ) {
2147        if ( MaxFacilitiesInTable >= 0 && i >= MaxFacilitiesInTable ) {
2148                fprintf( htmlfile,"<tr bgcolor=\"#FF6666\"><td colspan=\"%d\"><font size=\"%d\" face=\"Sans-serif\">%d more facilities (not listed)</font></td></tr>", damTblColumns, facility_font_size, dam_close_count-i );
2149            break;
2150        }
2151        if ( i == damMapLimit ) {
2152                fprintf( htmlfile,"<tr bgcolor=\"#FF6666\"><td colspan=\"%d\"><font size=\"%d\" face=\"Sans-serif\">Facilities below this point will not appear on map</font></td></tr>", damTblColumns, facility_font_size );
2153        }
2154        fprintf( htmlfile,"<tr bgcolor=\"#%s\">", i%2==0 ? "DDDDFF" : "FFFFFF");
2155        if ( MapQuestKey[0] == 0 && i < 26 ) {
2156                fprintf( htmlfile, "<td><font size=\"%d\" face=\"Sans-serif\">%c</font></td>", facility_font_size, i+'A' );
2157        } else {
2158                fprintf( htmlfile, "<td><font size=\"%d\" face=\"Sans-serif\">%d</font></td>", facility_font_size, i+1 );
2159        }
2160                fprintf( htmlfile, "<td><font size=\"%d\" face=\"Sans-serif\">%s</font></td>", facility_font_size, dam_order[i]->name );
2161        if ( ShowRegionAndArea ) {
2162            fprintf( htmlfile, "<td><font size=\"%d\" face=\"Sans-serif\">%s</font></td>", facility_font_size, region_names[dam_order[i]->region_id] );
2163            fprintf( htmlfile, "<td><font size=\"%d\" face=\"Sans-serif\">%s</font></td>", facility_font_size, area_abbrs[dam_order[i]->area_id] );
2164        }
2165        fprintf( htmlfile, "<td style=\"text-align:right\"><font size=\"%d\" face=\"Sans-serif\">%1.1lf</font></td>", facility_font_size, dam_order[i]->dist);
2166        if ( ShowMiles )
2167                fprintf( htmlfile, "<td style=\"text-align:right\"><font size=\"%d\" face=\"Sans-serif\">%1.1lf</font></td>", facility_font_size, dam_order[i]->dist*KM2MILES);
2168        if ( ReportEstimatedPGAs ) {
2169            ePGA = EstimatePGA(  dam_order[i]->lat, dam_order[i]->lon, arc->sum.lat,  arc->sum.lon, arc->sum.z, arc->sum.Mpref );
2170            if ( ePGA < 0 ) {
2171                strcpy( temp, "N/A" );
2172                        } else {
2173                sprintf( temp, "%1.6lf", ePGA*100 );
2174                        }
2175            if (ePGA > *max_epga_g)
2176                *max_epga_g = ePGA;
2177            fprintf( htmlfile, "<td style=\"text-align:right\"><font size=\"%d\" face=\"Sans-serif\">%s</font></td>", facility_font_size, temp);
2178            if ( ePGA >= 0 ) {
2179                sprintf( temp, "%1.6lf", ePGA );
2180                        }
2181            fprintf( htmlfile, "<td style=\"text-align:right\"><font size=\"%d\" face=\"Sans-serif\">%s</font></td>", facility_font_size, temp);
2182        }
2183        fprintf( htmlfile, "</tr>\n" );
2184    }
2185    fprintf( htmlfile, "</table><br/><br/>\n" );
2186
2187        return 0;
2188}
2189/* the magnitude type from hypoinverse: need to change if ML gets produced in future */
2190#define MAG_TYPE_STRING "Md"
2191#define MAG_MSG_TYPE_STRING "ML"
2192#define MAG_MSG_MWTYPE_STRING "Mw"
2193
2194void InsertHeaderTable(FILE *htmlfile, HypoArc *arc, char Quality, int showDM, char *scnl, SM_INFO *sminfo ) {
2195    char        timestr[80], timestrUTC[80];                    /* Holds time messages */
2196    time_t      ot = ( time_t )( arc->sum.ot - GSEC1970 );
2197    //struct tm     *timeinfo;
2198    struct tm   mytimeinfo;
2199    char        *grname[36];          /* Flinn-Engdahl region name */
2200    int parity = 0, i;
2201    char        *bg[2] = {" bgcolor=\"DDDDFF\" class=\"alt\"", ""};
2202    char        *timeName;
2203
2204    if ( formatTimestamps( ot, arc->sum.lat, arc->sum.lon, timestr, timestrUTC ) ) {
2205//         ot = ( time_t )( arc->sum.ot - GSEC1970 );
2206        //timeinfo =
2207        localtime_ew ( &ot, &mytimeinfo );
2208        strftime( timestr, 80, "%Y.%m.%d %H:%M:%S (local to server)", &mytimeinfo ); // Prepare origin time (local)
2209
2210        //timeinfo =
2211        gmtime_ew ( &ot, &mytimeinfo );
2212        strftime( timestrUTC, 80, "%Y.%m.%d %H:%M:%S", &mytimeinfo ); // Prepare origin time (UTC)
2213    }
2214
2215    // Table header
2216    fprintf( htmlfile, "<table id=\"DataTable\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n" );
2217    if(strlen(DataCenter) > 0 )
2218    {
2219        fprintf( htmlfile, "<tr bgcolor=\"000060\"><th><font size=\"3\" face=\"Sans-serif\" color=\"FFFFFF\">Data Center: %s</font><th><tr>\n", DataCenter );
2220    }
2221    if ( scnl ) {
2222        fprintf( htmlfile, "<tr bgcolor=\"000060\"><th><font size=\"3\" face=\"Sans-serif\" color=\"FFFFFF\">Threshold Trigger Exceedance</font><th><tr>\n" );
2223
2224        fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">Source:</font></td><td><font size=\"3\" face=\"Sans-serif\">%s</font></td><tr>\n", bg[parity], scnl );
2225        parity = 1-parity;
2226    } else {
2227        // Event ID
2228        fprintf( htmlfile, "<tr bgcolor=\"000060\"><th><font size=\"3\" face=\"Sans-serif\" color=\"FFFFFF\">EW Event ID: %ld</font><th><tr>\n", arc->sum.qid );
2229
2230        // NEIC id
2231        if ( neic_id[0] ) {
2232            fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">NEIC ID:</font></td><td><font size=\"3\" face=\"Sans-serif\"><a href=\"http://earthquake.usgs.gov/earthquakes/eventpage/%s\">%s</a></font></td><tr>\n", bg[parity], neic_id, neic_id );
2233            parity = 1-parity;
2234        }
2235    }
2236
2237    timeName = ( neic_id[0] == 0 ) ? "Trigger" : "Origin";
2238
2239    // Origin time (local)
2240    fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">%s time (local):</font></td><td><font size=\"3\" face=\"Sans-serif\">%s</font></td><tr>\n",
2241             bg[parity], timeName, timestr );
2242    parity = 1-parity;
2243
2244    // Origin time (UTC)
2245    fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">%s time (UTC):</font></td><td><font size=\"3\" face=\"Sans-serif\">%s</font></td><tr>\n",
2246             bg[parity], timeName, timestrUTC );
2247    parity = 1-parity;
2248
2249    // Seismic Region
2250    if (UseRegionName && ( arc->sum.lat != -1 || arc->sum.lon != -1 )) {
2251        FlEngLookup(arc->sum.lat, arc->sum.lon, grname, NULL);
2252        fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">Location:</font></td><td><font size=\"3\" face=\"Sans-serif\">%-36s</font></td><tr>\n",
2253                 bg[parity], *grname );
2254        parity = 1-parity;
2255    }
2256
2257    if ( arc->sum.lat != -1 || arc->sum.lon != -1 ) {
2258        // Latitude
2259        fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">Latitude:</font></td><td><font size=\"3\" face=\"Sans-serif\">%7.4f</font></td><tr>\n",
2260                 bg[parity], arc->sum.lat );
2261        parity = 1-parity;
2262
2263        // Longitude
2264        fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">Longitude:</font></td><td><font size=\"3\" face=\"Sans-serif\">%8.4f</font></td><tr>\n",
2265                 bg[parity],
2266                 arc->sum.lon );
2267        parity = 1-parity;
2268    }
2269
2270    if ( showDM ) {
2271        // Depth
2272        fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">Depth:</font></td><td><font size=\"3\" face=\"Sans-serif\">%4.1f km</font></td><tr>\n",
2273                 bg[parity], arc->sum.z );
2274        parity = 1-parity;
2275
2276        fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">Magnitude:</font></td>"
2277                 "<td><font size=\"3\" face=\"Sans-serif\">%4.1f</font></td><tr>\n",
2278                 bg[parity], arc->sum.Mpref );
2279        parity = 1-parity;
2280    }
2281    if ( sminfo ) {
2282        fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">PGA:</font></td>"
2283                 "<td><font size=\"3\" face=\"Sans-serif\">%4.1lf</font></td><tr>\n",
2284                 bg[parity], sminfo->pga );
2285        parity = 1-parity;
2286        fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">PGV:</font></td>"
2287                 "<td><font size=\"3\" face=\"Sans-serif\">%4.1lf</font></td><tr>\n",
2288                 bg[parity], sminfo->pgv );
2289        parity = 1-parity;
2290        fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">PGD:</font></td>"
2291                 "<td><font size=\"3\" face=\"Sans-serif\">%4.1lf</font></td><tr>\n",
2292                 bg[parity], sminfo->pgd );
2293        parity = 1-parity;
2294        for ( i=0; i<sminfo->nrsa; i++ ) {
2295            fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">RSA %d:</font></td>"
2296                     "<td><font size=\"3\" face=\"Sans-serif\">%4.1lf</font></td><tr>\n",
2297                     bg[parity], i+1, sminfo->rsa[i]);
2298            parity = 1-parity;
2299        }
2300    }
2301
2302
2303    /* Event details
2304     ***************/
2305    if( ShowDetail )
2306    {
2307
2308        // RMS
2309        fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">RMS Error:</font></td><td><font size=\"3\" face=\"Sans-serif\">%5.2f s</font></td><tr>\n",
2310                 bg[parity], arc->sum.rms);
2311        parity = 1-parity;
2312
2313        // Horizontal error
2314        fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">Horizontal Error:"
2315                 "</font></td><td><font size=\"3\" face=\"Sans-serif\">%5.2f km</font></td><tr>\n",
2316                 bg[parity], arc->sum.erh );
2317        parity = 1-parity;
2318
2319        // Vertical error
2320        fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">Depth Error:</font></td><td><font size=\"3\" face=\"Sans-serif\">%5.2f km</font></td><tr>\n",
2321                 bg[parity], arc->sum.erz );
2322        parity = 1-parity;
2323
2324        // Azimuthal gap
2325        fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">Azimuthal Gap:</font></td>"
2326                 "<td><font size=\"3\" face=\"Sans-serif\">%d Degrees</font></td><tr>\n",
2327                 bg[parity], arc->sum.gap );
2328        parity = 1-parity;
2329
2330        // Number of phases
2331        fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">Total Phases:</font></td><td><font size=\"3\" face=\"Sans-serif\">%d</font></td><tr>\n",
2332                 bg[parity], arc->sum.nphtot);
2333        parity = 1-parity;
2334
2335        // Used phases
2336        fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">Total Phases Used:</font></td>"
2337                 "<td><font size=\"3\" face=\"Sans-serif\">%d</font></td><tr>\n",
2338                 bg[parity], arc->sum.nph );
2339        parity = 1-parity;
2340
2341        // Number of S phases
2342        fprintf( htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">Num S Phases Used:</font></td><td><font size=\"3\" face=\"Sans-serif\">%d</font></td><tr>\n",
2343                 bg[parity], arc->sum.nphS );
2344        parity = 1-parity;
2345
2346        // Average quality
2347        fprintf(htmlfile, "<tr%s><td><font size=\"3\" face=\"Sans-serif\">Quality:</font></td>"
2348                "<td><font size=\"3\" face=\"Sans-serif\">%c</font></td><tr>\n",
2349                bg[parity], Quality);
2350        parity = 1-parity;
2351    }
2352
2353    // Finish reference table
2354    fprintf( htmlfile, "</table><br/><br/>\n" );
2355}
2356
2357/* this is a squished down table  for the header info*/
2358void InsertShortHeaderTable(FILE *htmlfile, HypoArc *arc,char Quality ) {
2359    char        timestr[80];                    /* Holds time messages */
2360    time_t      ot;
2361    struct tm   mytimeinfo; //, *timeinfo;
2362    char        *grname[36];          /* Flinn-Engdahl region name */
2363    struct tm * (*timefunc)(const time_t *, struct tm *);
2364    char        time_type[30];                  /* Time type UTC or local */
2365    int parity = 0;
2366    char        *bg[2] = {" bgcolor=\"DDDDFF\" class=\"alt\"", ""};
2367
2368    timefunc = localtime_ew;
2369    if( UseUTC )
2370    {
2371        timefunc = gmtime_ew;
2372        strcpy( time_type, "UTC" );
2373    }
2374    ot = ( time_t )( arc->sum.ot - GSEC1970 );
2375    //timeinfo =
2376    timefunc ( &ot, &mytimeinfo );
2377    //memcpy( &mytimeinfo, timeinfo, sizeof(mytimeinfo) );
2378    strftime( timestr, 80, "%Y.%m.%d %H:%M:%S", &mytimeinfo ); // Prepare origin time
2379
2380    // Table header
2381    fprintf( htmlfile, "<table id=\"DataTable\" border=\"0\" cellspacing=\"1\" cellpadding=\"0\" width=\"600px\">\n" );
2382    if(strlen(DataCenter) > 0 )
2383    {
2384        fprintf( htmlfile,
2385                 "<tr bgcolor=\"000060\">"
2386                 "<th colspan=4><font size=\"3\" face=\"Sans-serif\" color=\"FFFFFF\">Data Center: %s</font></th>"
2387                 "</tr>\n", DataCenter );
2388    }
2389    // Event ID
2390    fprintf( htmlfile,
2391             "<tr bgcolor=\"000060\">"
2392             "<th colspan=4><font size=\"3\" face=\"Sans-serif\" color=\"FFFFFF\">EW Event ID: %ld</font></th>"
2393             "</tr>\n", arc->sum.qid );
2394    // Seismic Region
2395    if (UseRegionName && ( arc->sum.lat != -1 || arc->sum.lon != -1 )) {
2396        FlEngLookup(arc->sum.lat, arc->sum.lon, grname, NULL);
2397        fprintf( htmlfile,
2398                 "<tr%s>"
2399                 "<td colspan=2><font size=\"3\" face=\"Sans-serif\"><b>Location:</b></font></td>"
2400                 "<td colspan=2><font size=\"3\" face=\"Sans-serif\">%-36s</font></td>"
2401                 "</tr>\n",
2402                 bg[parity],
2403                 *grname );
2404        parity = 1-parity;
2405    }
2406
2407
2408    // Origin time
2409    fprintf( htmlfile,
2410             "<tr%s>"
2411             "<td><font size=\"3\" face=\"Sans-serif\"><b>Origin time:</b></font></td>"
2412             "<td><font size=\"3\" face=\"Sans-serif\">%s %s</font></td>\n",
2413             bg[parity], timestr, time_type );
2414    parity = 1-parity;
2415    // RMS
2416    fprintf( htmlfile,
2417             "<td><font size=\"3\" face=\"Sans-serif\"><b>RMS Error:</b></font></td>"
2418             "<td><font size=\"3\" face=\"Sans-serif\">%5.2f s</font></td>"
2419             "</tr>\n",
2420             arc->sum.rms);
2421    // Latitude
2422    // Longitude
2423    if ( arc->sum.lat != -1 || arc->sum.lon != -1 ) {
2424        fprintf( htmlfile,
2425                 "<tr%s>"
2426                 "<td><font size=\"3\" face=\"Sans-serif\"><b>Latitude, Longitude:</b></font></td>"
2427                 "<td><font size=\"3\" face=\"Sans-serif\">%7.4f, %8.4f</font></td>\n",
2428                 bg[parity], arc->sum.lat, arc->sum.lon );
2429        parity = 1-parity;
2430    }
2431    // Horizontal error
2432    fprintf( htmlfile,
2433             "<td><font size=\"3\" face=\"Sans-serif\"><b>Horizontal Error:</b></font></td>"
2434             "<td><font size=\"3\" face=\"Sans-serif\">%5.2f km</font></td>"
2435             "</tr>\n", arc->sum.erh );
2436
2437    // Depth
2438    fprintf( htmlfile,
2439             "<tr%s>"
2440             "<td><font size=\"3\" face=\"Sans-serif\"><b>Depth:</b></font></td>"
2441             "<td><font size=\"3\" face=\"Sans-serif\">%4.1f km</font></td>",
2442             bg[parity], arc->sum.z );
2443    parity = 1-parity;
2444    // Vertical error
2445    fprintf( htmlfile,
2446             "<td><font size=\"3\" face=\"Sans-serif\"><b>Depth Error:</b></font></td>"
2447             "<td><font size=\"3\" face=\"Sans-serif\">%5.2f km</font></td>"
2448             "</tr>\n",
2449             arc->sum.erz );
2450
2451    // Coda magnitude
2452    if (arc->sum.mdwt == 0 && DontShowMd == 0) {
2453        fprintf( htmlfile,
2454                 "<tr%s>"
2455                 "<td><font size=\"3\" face=\"Sans-serif\"><b>Coda Magnitude:</b></font></td>"
2456                 "<td><font size=\"3\" face=\"Sans-serif\">N/A %s</font></td>"
2457                 "<td><font size=\"3\" face=\"Sans-serif\">nobs=0</font></td>"
2458                 "</tr>\n",
2459                 bg[parity], MAG_TYPE_STRING );
2460        parity = 1-parity;
2461    } else if (DontShowMd == 0) {
2462        fprintf( htmlfile,
2463                 "<tr%s>"
2464                 "<td><font size=\"3\" face=\"Sans-serif\"><b>Coda Magnitude:</b></font></td>"
2465                 "<td><font size=\"3\" face=\"Sans-serif\">%4.1f %s</font></td>"
2466                 "<td><font size=\"3\" face=\"Sans-serif\">nobs=%d</font></td>"
2467                 "</tr>\n",
2468                 bg[parity], arc->sum.Mpref, MAG_TYPE_STRING, (int) arc->sum.mdwt );
2469        parity = 1-parity;
2470    }
2471
2472    // Average quality
2473    fprintf( htmlfile,
2474             "<tr%s>"
2475             "<td><font size=\"3\" face=\"Sans-serif\"><b>Quality:</b></font></td>"
2476             "<td><font size=\"3\" face=\"Sans-serif\">%c</font></td>", bg[parity], Quality);
2477    parity = 1-parity;
2478
2479    // Azimuthal gap
2480    fprintf( htmlfile,
2481             "<td><font size=\"3\" face=\"Sans-serif\"><b>Azimuthal Gap:</b></font></td>"
2482             "<td><font size=\"3\" face=\"Sans-serif\">%d Degrees</font></td>"
2483             "</tr>\n", arc->sum.gap );
2484
2485    // Number of phases
2486    fprintf( htmlfile,
2487             "<tr%s>"
2488             "<td><font size=\"3\" face=\"Sans-serif\"><b>Total Phases:</b></font></td>"
2489             "<td><font size=\"3\" face=\"Sans-serif\">%d</font></td>", bg[parity], arc->sum.nphtot);
2490    parity = 1-parity;
2491
2492    // Used phases
2493    fprintf( htmlfile,
2494             "<td><font size=\"3\" face=\"Sans-serif\"><b>Total Phases Used:</b></font></td>"
2495             "<td><font size=\"3\" face=\"Sans-serif\">%d</font></td>"
2496             "</tr>\n", arc->sum.nph );
2497
2498    // Number of S phases
2499    fprintf( htmlfile,
2500             "<tr%s>"
2501             "<td><font size=\"3\" face=\"Sans-serif\"><b>Num S Phases Used:</b></font></td>"
2502             "<td><font size=\"3\" face=\"Sans-serif\">%d</font> </td>"
2503             "</tr>\n",
2504             bg[parity], arc->sum.nphS );
2505    parity = 1-parity;
2506
2507
2508    // Finish reference table
2509    fprintf( htmlfile, "</table><br/><br/>\n" );
2510}
2511
2512int compareSiteOrder( const void*site_p_1, const void*site_p_2 ) {
2513    double diff = ((SiteOrder*)site_p_1)->key - ((SiteOrder*)site_p_2)->key;
2514    return diff<0 ? -1 : diff>0 ? 1 : ((SiteOrder*)site_p_1)->idx - ((SiteOrder*)site_p_2)->idx;
2515}
2516
2517int compareDamOrder( const void*dam_p_1, const void*dam_p_2 ) {
2518    DamInfo* dip1 = *((DamInfo**)dam_p_1);
2519    DamInfo* dip2 = *((DamInfo**)dam_p_2);
2520    double diff = dip1->dist - dip2->dist;
2521    int rv = diff<0 ? -1 : diff>0 ? 1 : 0;
2522    return rv;
2523}
2524
2525int callback1(void* notUsed, int argc, char** argv, char** azColName  ) {
2526    char *p;
2527    //notUsed = 0;
2528    strcpy( neic_id, argv[0] );
2529    p = neic_id;
2530    while ( *p != 0 && *p != ' ' )
2531        p++;
2532    *p = 0;
2533    return 0;
2534}
2535
2536int shouldSendEmail( int i, double distance_from_center, 
2537    HypoArc *arc, HypoArcSM2 *arcsm,
2538    uint32_t subRegionsUsed, uint64_t subAreasUsed ) 
2539{
2540    int j,k;
2541    int reg_match, area_match, dam_match, loc_match, mag_match;
2542    if ( !emailrecipients[i].subscription ) {
2543        /* Skip recipent if not subscribed & not on mailing list for this type of event */
2544        if ( ( (arcsm->fromArc && !emailrecipients[i].sendOnEQ) ||
2545                    (!arcsm->fromArc && !emailrecipients[i].sendOnSM) ) ) {
2546            logit("et", "gmewhtmlemail: No email sent to %s because wrong type of event\n",
2547                      emailrecipients[i].address);
2548            return 0;
2549        }
2550
2551        /* Skip recipent if not subscribed & not on mailing list for this type of event */
2552        if ( ( emailrecipients[i].max_distance != OTHER_WORLD_DISTANCE_KM &&
2553                    emailrecipients[i].max_distance < distance_from_center ) ) {
2554            logit("et", "gmewhtmlemail: No email sent to %s because distance of event %5.1f higher than threshold %5.1f\n",
2555                  emailrecipients[i].address, distance_from_center, emailrecipients[i].max_distance);
2556            return 0;
2557        }
2558    } else {
2559        // Skip recipient if subscribed and doesn't meet criteria
2560        reg_match = ((emailrecipients[i].subRegions & subRegionsUsed) != 0);
2561        area_match = ((emailrecipients[i].subAreas & subAreasUsed) != 0);
2562        dam_match = 0;
2563        if ( !(reg_match || area_match) )
2564            for ( j=0; !dam_match && j<dam_sub_close_count; j++ )
2565                for ( k=0; !dam_match && k<emailrecipients[i].numDams; k++ )
2566                    if ( emailrecipients[i].subDams[k] == dam_order[j]->dam_id )
2567                        dam_match = 1;
2568        mag_match = (emailrecipients[i].min_magnitude==0 || emailrecipients[i].min_magnitude <= arc->sum.Mpref); 
2569        loc_match = (reg_match || area_match || dam_match);             
2570
2571        if ( (loc_match==0) || (loc_match!=0 && mag_match==0) ) {
2572            logit("et", "gmewhtmlemail: No email sent to %s because no match on subscription criteria\n",
2573                      emailrecipients[i].address);
2574                        //XXX XXX TODO: REMOVE!!!
2575            logit("et", "gmewhtmlemail: min_magnitude: %lf numRegions: %d numAreas: %d numDams: %d subRegionsUsed: %u subAreasUsed: %llu\n", 
2576                 emailrecipients[i].min_magnitude, emailrecipients[i].numRegions, emailrecipients[i].numAreas, 
2577                 emailrecipients[i].numDams, subRegionsUsed, subAreasUsed);
2578            return 1;
2579         }
2580     }
2581     return 1;
2582}
2583
2584void prepare_dam_info( HypoArc *arc, 
2585    char*       regionsStr,     char*       regionsUsed,
2586    uint32_t*   subRegionsUsed, uint64_t*   subAreasUsed , int MaxDist)
2587{
2588    int i, closeEnough = 1;
2589   
2590    for ( i=0; i<dam_count; i++ )
2591        dam_order[i]->dist = distance( arc->sum.lat, arc->sum.lon,
2592                                    dam_order[i]->lat, dam_order[i]->lon, 'K' );
2593    qsort( dam_order, dam_count, sizeof(dam_order[0]), &compareDamOrder );
2594    dam_close_count = dam_count;
2595    dam_sub_close_count = dam_count;
2596    for ( i=0; closeEnough && (i<dam_count); i++ ) { 
2597        closeEnough = 0;
2598        if ( dam_order[i]->dist <= MaxDist ) {
2599            dam_close_count = i+1;
2600            regionsUsed[dam_order[i]->region_id] = 1;
2601            closeEnough = 1;
2602        }       
2603        if ( dam_order[i]->dist <= MaxDist ) {
2604            dam_sub_close_count = i+1;
2605            *subRegionsUsed |= (1<<dam_order[i]->region_id);
2606            *subAreasUsed |= (UINT64_C(1)<<dam_order[i]->area_id);
2607            closeEnough = 1;
2608        }       
2609    }
2610}
2611
2612
2613
2614/******************************************************************************
2615 * process_message() Processes a message to find if its a real event or not   *
2616 ******************************************************************************/
2617int process_message( int arc_index ) {
2618    HypoArcSM2  *arcsm = arc_list[arc_index];
2619    HypoArc     *arc = &(arcsm->ha);
2620    double      starttime = 0;                  /* Start time for all traces */
2621    double      endtime = 0;                    /* End time for all traces */
2622    double      dur;                            /* Duration of the traces */
2623    int         i, pos;                      /* Generic counters */
2624    int         gsamples[MAX_GET_SAMP];         /* Buffer for resampled traces */
2625    char        chartreq[50000];                /* Buffer for chart requests or GIFs */     /* 2013.05.29 for base64 */
2626    SITE        *sites[MAX_STATIONS];           /* Selected sites */
2627    double      coda_mags[MAX_STATIONS];        /* station coda mags > 0 if used */
2628    int         coda_weights[MAX_STATIONS];     /* Coda Weight codes for each trace */
2629    double      distances[MAX_STATIONS];        /* Distance from each station */
2630    SM_INFO     *smForSite[MAX_STATIONS];
2631    SiteOrder   site_order[MAX_STATIONS];
2632    SITE        *sorted_sites[MAX_STATIONS];
2633    SITE        *waveSite, triggerSite;
2634    char        *cPtr, phaseName[2];
2635
2636    struct tm * (*timefunc)(const time_t *, struct tm *);
2637    char        time_type[30];                  /* Time type UTC or local */
2638    int         nsites = 0;                     /* Number of stations in the arc msg */
2639    char        system_command[MAX_STRING_SIZE];/* System command to call email prog */
2640    char        kml_filename[MAX_STRING_SIZE];  /* Name of kml file to be generated */
2641    FILE        *htmlfile;                      /* HTML file */
2642    FILE        *header_file;                   /* Email header file */
2643    FILE        *event_address_file;            /* File mapping events to addresses notified in case of deletion */
2644    time_t      ot, st;                         /* Times */
2645    struct tm   mytimeinfo; //, *timeinfo;
2646    char        timestr[80];                    /* Holds time messages */
2647    char        eventAddrName[250];             /* name of event_address_file */
2648    char        fullFilename[250];              /* Full html file name */
2649    char        hdrFilename[250];               /* Full header file name */
2650    char        *buffer;                        /* Buffer for raw tracebuf2 messages */
2651    int         bsamplecount;                   /* Length of buffer */
2652    char        Quality;        /* event quality */
2653    FILE*       gifHeader;                      /* Header for GIF attachments with sendmail */
2654    char        gifHeaderFileName[257];         /* GIF Header filename */
2655    int         rv = TRUE;
2656    char        temp[MAX_GET_CHAR];
2657    char        *request = chartreq;
2658    char        timestrUTC[80];                    /* Holds time messages */
2659    char        regionsStr[MAX_STRING_SIZE];
2660    char        regionsUsed[MAX_REGIONS] = {0};
2661    uint32_t    subRegionsUsed = 0;
2662    uint64_t    subAreasUsed = 0;
2663    double      maxmin[2];
2664    char        hasWaveforms = ((arcsm->fromArc) && (arcsm->sm_count > 0));
2665    int         ix;
2666    double      max_dist, max_epga_g, max_pga, max_dist_pad, max_pga_pad, max_pga_ix;
2667    SiteOrder   snl_order[MAX_STATIONS];
2668    int         snl_sites;
2669    sqlite3     *db;
2670    char        *err_msg = NULL;
2671    int         ignore_PGA_threshold = 0; /* this gets set to 1 for this event if any MagDist evaluation is exceeded */
2672    int         distance_max; /* the actual distance maximum to use for facility checks */
2673
2674
2675    distance_max = MaxFacilityDist; 
2676    if (NMagDist > 0) {
2677        for(i=0; i< NMagDist; i++) {
2678             if (arc->sum.Mpref >= MagDist[i].mag) {
2679                   distance_max = MagDist[i].dist;
2680                   ignore_PGA_threshold = 1;
2681                   logit("t", "Resetting MaxFacilitDist to %d because magnitude of event %f greater than MagDist magnitude %f\n",
2682                            distance_max, arc->sum.Mpref, MagDist[i].mag);
2683             }
2684        }
2685    }
2686
2687    max_epga_g=0.0; /* max Estimated PGA in units of g */
2688
2689    if ( dbAvailable ) {
2690        /* get the NEIC ID for this event stored in the SQLite db  created by ark2arc */
2691        rv = sqlite3_open(db_path, &db);
2692
2693        if (rv != SQLITE_OK) {
2694            logit("et", "gmewhtmlemail: Cannot open database: %s\n", sqlite3_errmsg(db));
2695        } else {
2696            char evt_sql[200];
2697            neic_id[0] = 0;
2698            sprintf( evt_sql, "SELECT PdlID FROM Events WHERE ROWID = %ld", arc->sum.qid );
2699            rv = sqlite3_exec(db, evt_sql, callback1, 0, &err_msg);
2700            sqlite3_close(db);
2701        }
2702    }
2703
2704    /* Initialize kml file name and time type
2705     ****************************************/
2706    kml_filename[0] = 0;
2707    strcpy( time_type, "Local Time" );
2708    starttime = arc->sum.ot;
2709
2710    /* Read phases using read_arc.c function
2711     * For each phase, try to find the station in the sites file
2712     * Then store phase data (arrival, type, coda length, etc)
2713     * and update starttime and endtime to include all phases
2714     * and corresponding coda lengths.
2715     ***********************************************************/
2716    for (i=0; i<arcsm->sm_count; i++)
2717    {
2718
2719        // Search this site on the sites file
2720        pos = site_index( arcsm->sm_arr[i].sta,
2721                          arcsm->sm_arr[i].net,
2722                          arcsm->sm_arr[i].comp,
2723                          arcsm->sm_arr[i].loc[0] ? arcsm->sm_arr[i].loc : "--") ;
2724        // If site is not found, continue to next phase
2725        if( pos == -1 )
2726        {
2727            logit( "et", "gmewhtmlemail: Unable to find %s.%s.%s.%s (%d) on the site file\n",
2728                   arcsm->sm_arr[i].sta,
2729                   arcsm->sm_arr[i].comp,
2730                   arcsm->sm_arr[i].net,
2731                   arcsm->sm_arr[i].loc,
2732                   i
2733                 );
2734            continue;
2735        }
2736        if ( arcsm->sm_arr[i].pga < IgnorePGABelow )
2737        {
2738            logit( "et", "gmewhtmlemail: PGA too low for %s.%s.%s.%s (%d); ignored\n",
2739                   arcsm->sm_arr[i].sta,
2740                   arcsm->sm_arr[i].comp,
2741                   arcsm->sm_arr[i].net,
2742                   arcsm->sm_arr[i].loc,
2743                   i
2744                 );
2745            continue;
2746        }
2747
2748
2749        // New station, store its pointer
2750        sites[nsites] = &Site[pos];
2751
2752        // Store epicentral distance
2753        site_order[nsites].key = distances[nsites] = distance( arc->sum.lat, arc->sum.lon,
2754                                 Site[pos].lat, Site[pos].lon, 'K' );
2755
2756        if ( distances[nsites] > (MaxStationDist>distance_max?MaxStationDist:distance_max) )
2757            continue;
2758        site_order[nsites].idx = nsites;
2759        smForSite[nsites] = arcsm->sm_arr+i;
2760
2761        // Increment nsites
2762        nsites++;
2763
2764        /* Check if the number of sites exceeds the
2765         * predefined maximum number of stations
2766         ******************************************/
2767        if( nsites >= MAX_STATIONS )
2768        {
2769            logit( "et", "gmewhtmlemail: More than %d stations in message\n",
2770                   MAX_STATIONS );
2771            break;
2772        }
2773    } // End of 'for' cycle for processing the phases in the arc message
2774
2775        if (nsites == 0) {
2776                if (Debug) logit( "et", "gmewhtmlemail: No stations to available, no event email will be sent\n");
2777                return 0;
2778        }
2779
2780    qsort( site_order, nsites, sizeof(SiteOrder), &compareSiteOrder );
2781    for ( i=0; i<nsites; i++ )
2782        sorted_sites[i] = sites[site_order[i].idx];
2783
2784    /* Correct times for epoch 1970
2785     ******************************/
2786    endtime = starttime;
2787    starttime -= GSEC1970;      // Correct for epoch
2788
2789    ot = time( NULL );
2790
2791    if ( IgnoreHours>0 && (ot - starttime)/3600 > IgnoreHours ) {
2792        logit("o", "gmewhtmlemail: Ignoring event more than %1.2lf hours old (%1.2lf hours)\n", IgnoreHours, (ot - starttime)/3600 );
2793        return 0;
2794    }
2795
2796    starttime -= TimeMargin;    // Add time margin
2797    endtime -= GSEC1970;        // Correct for epoch
2798    endtime += TimeMargin;      // Add time margin
2799
2800    /* Check maximum duration of the traces
2801     **************************************/
2802    dur = endtime - starttime;
2803    if( 1 ) { // dur > ( double ) DurationMax )
2804        endtime = starttime+DurationMax;
2805        dur = DurationMax;  /* this is used later for header of waveform table */
2806    }
2807
2808    /* Change to UTC time, if required
2809     *********************************/
2810    timefunc = localtime_ew;
2811    if( UseUTC ) {
2812        timefunc = gmtime_ew;
2813        strcpy( time_type, "UTC" );
2814    }
2815
2816    /* Log debug info
2817     ****************/
2818    if( Debug ) {
2819        logit("o", "Available channels (%d):\n", nsites);
2820        for( i = 0; i < nsites; i++ )
2821            logit( "o", "%5s.%3s.%2s.%2s\n",
2822                   sorted_sites[i]->name, sorted_sites[i]->comp,
2823                   sorted_sites[i]->net, sorted_sites[i]->loc);
2824
2825        logit( "o", "Time margin: %f\n", TimeMargin );
2826
2827        ot = ( time_t ) starttime;
2828        timefunc ( &ot, &mytimeinfo );
2829        strftime( timestr, 80, "%Y.%m.%d %H:%M:%S", &mytimeinfo );
2830        logit( "o", "Waveform starttime: %s %s\n", timestr, time_type );
2831
2832        ot = ( time_t ) endtime;
2833        timefunc ( &ot, &mytimeinfo );
2834        strftime( timestr, 80, "%Y.%m.%d %H:%M:%S", &mytimeinfo );
2835        logit( "o", "Waveform endtime:   %s %s\n", timestr, time_type );
2836    }
2837
2838    prepare_dam_info( arc, regionsStr, regionsUsed, &subRegionsUsed, &subAreasUsed, distance_max);
2839
2840    if ( dam_close_count == 0 ) {
2841        logit("t", "gmewhtmlemail: No facilities within %d km; aborting email for event id: %ld\n", distance_max, arc->sum.qid);
2842        return 0;
2843    }
2844
2845    /* Build list of region codes into a string */
2846    strcpy( regionsStr, " (" );
2847    for ( i=0; region_order[i] != 99; i++ )
2848        if ( regionsUsed[region_order[i]] ) {
2849            strcat( regionsStr, region_abbrs[region_order[i]] );
2850            strcat( regionsStr, "," );
2851        }
2852    regionsStr[strlen(regionsStr)-1] = ')';
2853    regionsStr[strlen(regionsStr)] = 0;
2854
2855    /* At this point, we have retrieved the information required for the html email
2856     * traces, if there is any. Now, move forward to start producing the output files.
2857     *********************************************************************************/
2858
2859    /* build a KML file
2860     ******************/
2861    if( KMLdir[0] != 0 )
2862    {
2863        logit( "ot", "gmewhtmlemail: writing KML file.\n" );
2864        kml_writer( KMLdir, &(arc->sum), KMLpreamble, kml_filename, sorted_sites, nsites );
2865    }
2866
2867    /* Start html email file
2868     ***********************/
2869    if ( neic_id[0] ) {
2870        ot = ( time_t )( arc->sum.ot - GSEC1970 );
2871        gmtime_ew ( &ot, &mytimeinfo );
2872        strftime( timestrUTC, 80, "%Y%m%d%H%M%S", &mytimeinfo ); // Prepare origin time (UTC)
2873        snprintf(fullFilename, 250, "%s_%s_%s.html", HTMLFile, timestrUTC, neic_id ); // Name of html file
2874    } else
2875        snprintf(fullFilename, 250, "%s_%ld.html", HTMLFile, arc->sum.qid ); // Name of html file
2876    if( Debug ) logit( "ot", "Creating html file %s\n", fullFilename );
2877
2878    // Open html file
2879    if( (htmlfile = fopen( fullFilename, "w" )) != NULL )
2880    {
2881        /* Save html header
2882         ******************/
2883        if( Debug ) logit( "ot", "Writing html header %s\n", fullFilename );
2884        // Placing css in the html body instead of header, to bypass some email clients
2885        // blocking the style sheet
2886        fprintf( htmlfile, "<html>\n<header>\n<style type=\"text/css\">" );
2887
2888        /* Close style header
2889         ********************/
2890        fprintf( htmlfile, "\n</style>" );
2891
2892        /* Close HTML Header */
2893        fprintf( htmlfile, "</header>\n" );
2894
2895        /* Open HTML body */
2896        fprintf( htmlfile, "<body>\n" );
2897
2898        Quality = ComputeAverageQuality( arc->sum.rms, arc->sum.erh, arc->sum.erz,
2899                                         arc->sum.z, (float) (1.0*arc->sum.dmin), arc->sum.nph, arc->sum.gap );
2900
2901        /* Create table with reference information
2902         *****************************************/
2903                //This creates an event summary table named 'DataTable':
2904        if (ShortHeader) {
2905            InsertShortHeaderTable(htmlfile, arc, Quality);
2906        } else {
2907            char *sncl = arcsm->fromArc ? NULL : arcsm->sncl;
2908            InsertHeaderTable(htmlfile, arc, Quality, arcsm->fromArc, sncl, sncl!=NULL && nsites==1 ? smForSite[0] : NULL );
2909        }
2910
2911                //Output station table:
2912                if(InsertStationTable(htmlfile, arcsm->fromArc, nsites, site_order, sorted_sites, smForSite, &snl_sites, snl_order, distances) == -1) {
2913                        logit("et", "Failed to insert station table\n");       
2914                }
2915
2916                //Output dam table:
2917                if(InsertDamTable(htmlfile, arc, &max_epga_g) == -1) {
2918                        logit("et", "Failed to insert dam table\n");   
2919                }
2920
2921        /* Produce google map with the stations and hypocenter
2922         *****************************************************/
2923        i=-1;
2924        if( GMTmap ) {
2925            if( Debug ) logit( "ot", "Computing GMT map\n" );
2926            if((i=gmtmap(chartreq,sorted_sites,nsites,arc->sum.lat,arc->sum.lon,arc->sum.qid)) == 0)
2927                fprintf(htmlfile, "%s\n<br/><br/>", chartreq);
2928        }
2929
2930        if(i==-1) {
2931            if( Debug ) logit( "ot", "Computing map\n" );
2932            MapRequest( chartreq, sorted_sites, nsites, arc->sum.lat, arc->sum.lon, arcsm, site_order, snl_order );
2933            fprintf(htmlfile, "%s\n<br/><br/>", chartreq);
2934        }
2935
2936
2937        max_pga=0.0; /* max PGA in units of cm/s/s */
2938        if ( hasWaveforms ) {
2939            if( Debug ) logit( "ot", "Computing PGA chart\n" );
2940            ix = snl_order[snl_sites-1].idx;
2941            max_dist = distances[ix];
2942            max_dist_pad = max_dist*1.1;
2943
2944            /* Base of the google static chart
2945             *********************************/
2946            snprintf(request, MAX_GET_CHAR, "<img src=\"http://chart.apis.google.com/chart?chs=600x400&cht=s&chts=000000,24&chtt=Peak+PGA+vs.+Distance" );
2947
2948            /* Add distances (x axis)
2949             ************************/
2950            max_pga_ix = ix = snl_order[0].idx;
2951            max_pga = smForSite[ix]->pga;
2952                        if ( RestrictThresholds ) {
2953                                if (strcmp(RestrictedNetwork, smForSite[ix]->net) != 0) {
2954                                        max_pga = 0.0;  //effectively ignore initial PGA if it's not from network of interest
2955                                }
2956                        }
2957                        logit("t", "Starting max_pga for %s.%s.%s.%s pga=%lf distance=%lf\n", 
2958                                        smForSite[ix]->net, smForSite[ix]->sta, 
2959                                        smForSite[ix]->comp, smForSite[ix]->loc, 
2960                                        smForSite[ix]->pga, distances[ix] );
2961            snprintf( temp, MAX_GET_CHAR, "%s&chd=t:%lf", request, distances[ix]/max_dist_pad*100 );
2962            snprintf( request, MAX_GET_CHAR, "%s", temp );
2963            for( i = 1; i < snl_sites; i++ )
2964            {
2965                ix = snl_order[i].idx;
2966                snprintf( temp, MAX_GET_CHAR, "%s,%lf", request, distances[ix]/max_dist_pad*100 );
2967                snprintf( request, MAX_GET_CHAR, "%s", temp );
2968                                logit("t", "Testing max_pga for %s.%s.%s.%s pga=%lf distance=%lf\n", 
2969                                        smForSite[ix]->net, smForSite[ix]->sta, 
2970                                        smForSite[ix]->comp, smForSite[ix]->loc, 
2971                                        smForSite[ix]->pga, distances[ix] );
2972                                if ( RestrictThresholds && (strcmp(RestrictedNetwork, smForSite[ix]->net) != 0)) {
2973                                        continue;       
2974                                }
2975                if ( max_pga < smForSite[ix]->pga ) {
2976                    max_pga = smForSite[ix]->pga;
2977                    max_pga_ix = ix;
2978                }
2979            }
2980            max_pga_pad = max_pga*1.1;
2981
2982            /* Add PGAs (y axis)
2983             *******************/
2984            ix = snl_order[0].idx;
2985            snprintf( temp, MAX_GET_CHAR, "%s|%lf", request, smForSite[ix]->pga/max_pga_pad*100 );
2986            snprintf( request, MAX_GET_CHAR, "%s", temp );
2987            for( i = 1; i < snl_sites; i++ )
2988            {
2989                ix = snl_order[i].idx;
2990                snprintf( temp, MAX_GET_CHAR, "%s,%lf", request, smForSite[ix]->pga/max_pga_pad*100 );
2991                snprintf( request, MAX_GET_CHAR, "%s", temp );
2992            }
2993
2994            /* Add annotations
2995             *****************/
2996            snprintf( temp, MAX_GET_CHAR, "%s&chm=B,333333,0,0,7", request );
2997            snprintf( request, MAX_GET_CHAR, "%s", temp );
2998            for( i = 0; i < snl_sites; i++ ) {
2999                ix = snl_order[i].idx;
3000                snprintf( temp, MAX_GET_CHAR, "%s|A%s.%s.%s.%s,%s,0,%d,7", request,
3001                          smForSite[ix]->sta, smForSite[ix]->comp,
3002                          smForSite[ix]->net, smForSite[ix]->loc,
3003                          max_pga_ix==ix ? "990000" : "333333", i );
3004                snprintf( request, MAX_GET_CHAR, "%s", temp );
3005            }
3006
3007            /* Specify colors
3008             *****************/
3009            snprintf( temp, MAX_GET_CHAR, "%s&chco", request );
3010            snprintf( request, MAX_GET_CHAR, "%s", temp );
3011            for( i = 0; i < snl_sites; i++ ) {
3012                ix = snl_order[i].idx;
3013                snprintf( temp, MAX_GET_CHAR, "%s%c%s", request, i==0 ? '=' : '|',
3014                          max_pga_ix==ix ? "FF0000" : "000099" );
3015                snprintf( request, MAX_GET_CHAR, "%s", temp );
3016            }
3017
3018
3019            /* Specify axes
3020             **************/
3021            snprintf( temp, MAX_GET_CHAR, "%s&chxt=x,y,x,y&chxr=0,0,%lf|1,0,%lf&chxl=2:|Distance+(km)|3:|PGA|(cm/s/s)&chxp=2,50|3,52,48&chg=100,100,1,0", request, max_dist_pad, max_pga_pad);
3022            snprintf( request, MAX_GET_CHAR, "%s", temp );
3023
3024            /* End of the request
3025             ********************/
3026            snprintf( temp, MAX_GET_CHAR, "%s\"/>", request );
3027            snprintf( request, MAX_GET_CHAR, "%s", temp );
3028            fprintf(htmlfile, "%s\n<br/><br/>", request);
3029        } else if ( arcsm->fromArc ) {
3030            if( Debug ) logit( "ot", "No PGA chart, no sites with waveforms\n" );
3031            snprintf( temp, MAX_GET_CHAR, "<p>No sites to plot</p>" );
3032            snprintf( request, MAX_GET_CHAR, "%s", temp );
3033            fprintf(htmlfile, "%s\n<br/><br/>", request);
3034        }
3035
3036
3037        /* Reserve memory buffer for raw tracebuf messages
3038         *************************************************/
3039        if ( hasWaveforms && NoWaveformPlots == 0 ) {
3040            buffer = ( char* ) malloc( MAX_SAMPLES * 4 * sizeof( char ) );
3041            if( buffer == NULL )
3042            {
3043                logit( "et", "gmewhtmlemail: Cannot allocate buffer for traces\n" );
3044                // return -1; - Event if there is no memory for traces, still try to send email
3045            }
3046            else
3047            {
3048                /* Produce station traces
3049                 ************************/
3050                if( Debug ) logit("ot", "Computing Traces\n" );
3051
3052                // Save header of traces table
3053                st = ( time_t )starttime;
3054                gmtime_ew( &st, &mytimeinfo );
3055                strftime( timestr, 80, "%Y.%m.%d %H:%M:%S", &mytimeinfo );
3056                fprintf( htmlfile, "<table id=\"WaveTable\" width=\"%d;\">\n",
3057                         TraceWidth + 10 );
3058                fprintf( htmlfile, "<thead>\n" );
3059                fprintf( htmlfile, "<tr bgcolor=\"000060\"><th><font size=\"1\" face=\"Sans-serif\" color=\"FFFFFF\">Waveform%s: (StartTime: %s UTC, Duration: %d"
3060                         " seconds)</font></th></tr>\n", arcsm->fromArc ? "s" : "", timestr, (int) dur );
3061                fprintf( htmlfile, "</thead>\n" );
3062                fprintf( htmlfile, "</tbody>\n" );
3063
3064                /* Cycle for each station
3065                 *  - Try to retrieve waveform data from the wave servers
3066                 *  - Decide to produce GIF or google chart
3067                 *  - If GIF, call function to produce GIF file and the request is
3068                 *          the filename within <img> tags - Create GIF header
3069                 *  - If google char, resample the data and then produce the google chart
3070                 *          encoded GET request
3071                 ************************************************************************/
3072                if( UseGIF && !UseBlat )
3073                {
3074                    /* If using gifs with sendmail, we need to start writing a header file
3075                     * with the base64 data included
3076                     *******************************************************/
3077                    snprintf( gifHeaderFileName, 257, "%s_GifHeader", HTMLFile );
3078                    gifHeader = fopen( gifHeaderFileName, "w" );
3079                }
3080
3081                /* Loop over the stations or, if a triggered event, over the triggering station */
3082                for( ix = 0; (ix==0 && !arcsm->fromArc) || (ix < nsites); ix++ )
3083                {
3084                    if ( !arcsm->fromArc ) {
3085                        /* Fill triggerSite/waveSite with the triggering SCNL */
3086                        cPtr = arcsm->sncl;
3087                        i = 0;
3088                        while (*cPtr != '.')
3089                            triggerSite.name[i++] = *(cPtr++);
3090                        triggerSite.name[i++] = 0;
3091                        i = 0;
3092                        cPtr++;
3093                        while (*cPtr != '.')
3094                            triggerSite.net[i++] = *(cPtr++);
3095                        triggerSite.net[i++] = 0;
3096                        i = 0;
3097                        cPtr++;
3098                        while (*cPtr != '.')
3099                            triggerSite.comp[i++] = *(cPtr++);
3100                        triggerSite.comp[i++] = 0;
3101                        i = 0;
3102                        cPtr++;
3103                        while (*cPtr)
3104                            triggerSite.loc[i++] = *(cPtr++);
3105                        triggerSite.loc[i++] = 0;
3106                        waveSite = &triggerSite;
3107                        strcpy(phaseName, "T"); // this is legacy from ewhtmlemail and should be removed
3108                    } 
3109                    else {
3110                        i = site_order[ix].idx;
3111                        waveSite = sites[i];
3112                        strcpy(phaseName, "X"); // this is legacy from ewhtmlemail and should be removed
3113                    }
3114
3115                    /* Load data from waveservers
3116                     ****************************/
3117                    bsamplecount = MAX_SAMPLES * 4; //Sets number of samples back
3118                    if( Debug ) logit( "o", "Loading data from %s.%s.%s.%s\n",
3119                                           waveSite->name, waveSite->comp,
3120                                           waveSite->net, waveSite->loc );
3121                    if( getWStrbf( buffer, &bsamplecount, waveSite, starttime, endtime ) == -1 )
3122                    {
3123                        logit( "t", "gmewhtmlemail: Unable to retrieve data from waveserver"
3124                               " for trace from %s.%s.%s.%s\n",
3125                               waveSite->name, waveSite->comp,
3126                               waveSite->net, waveSite->loc );
3127                        continue;
3128                    }
3129
3130                                        /* Produce traces using EWAVE plotting service, drawing GIF or using Google Chart
3131                                         *********************************************************************************/
3132                                        if ( UseEWAVE )
3133                                        {
3134                                                if ( Debug ) {
3135                                                        logit( "et", "About to call writePlotRequest for station %s (smForSite->sta): %s\n", waveSite->name, smForSite[i]->sta);
3136                                                }
3137                                                if (writePlotRequest(chartreq, 50000, arc->sum.qid, ewaveAddr, ewavePort,
3138                                                                                        waveSite->net, waveSite->name, waveSite->loc, waveSite->comp,
3139                                                                                        st, (int)dur) < 0)
3140                                                {
3141                                                        //this is an error!
3142                                                        logit( "et", "Unable to write request for EWAVE plot of %s.%s.%s.%s\n",
3143                                                                                        waveSite->net, waveSite->name, waveSite->loc, waveSite->comp);
3144                                                        continue;
3145                                                }
3146                                        }
3147                                        else if( UseGIF )
3148                    {
3149                                                if (createGifPlot(chartreq, arc, buffer, bsamplecount, waveSite, starttime, endtime, phaseName) == -1) {
3150                                                        logit( "et", "Unable to create GIF plot for %s.%s.%s.%s\n",
3151                                                                                        waveSite->net, waveSite->name, waveSite->loc, waveSite->comp);
3152                                                        continue;       
3153                                                }
3154                    }
3155                    else
3156                    {
3157
3158                        /* Produce Google trace
3159                         **********************/
3160                        /* Resample trace
3161                         ****************/
3162                        if( Debug ) logit( "o", "Resampling samples for google chart\n" );
3163                        if( trbufresample( gsamples, MAX_GET_SAMP, buffer, bsamplecount,
3164                                           starttime, endtime, 30, maxmin ) == -1 ) // The value 30 comes from google encoding
3165                        {
3166                            logit( "e", "gmewhtmlemail: Error resampling samples from "
3167                                   "%s.%s.%s.%s\n",
3168                                   waveSite->name, waveSite->comp,
3169                                   waveSite->net, waveSite->loc );
3170                            continue;
3171                        }
3172
3173                        /* Produce google charts request
3174                         *******************************/
3175                        if( Debug ) logit( "o", "Creating google chart request\n" );
3176                        if( makeGoogleChart( chartreq, gsamples, MAX_GET_SAMP, phaseName,
3177                                             (((smForSite[i]->t)-starttime)/
3178                                              (endtime-starttime)*100),
3179                                             TraceWidth, TraceHeight, maxmin ) == -1 )
3180                        {
3181                            logit( "e", "gmewhtmlemail: Error generating google chart for "
3182                                   "%s.%s.%s.%s\n",
3183                                   waveSite->name, waveSite->comp,
3184                                   waveSite->net, waveSite->loc );
3185                            continue;
3186                        }
3187                        if( Debug ) logit( "o", "Produced google chart trace for %s.%s.%s.%s\n",
3188                                               waveSite->name, waveSite->comp,
3189                                               waveSite->net, waveSite->loc );
3190                    } // End of decision to make GIF or google chart traces
3191
3192                    /* Add to request to html file
3193                     *****************************/
3194                    ot = ( time_t ) smForSite[i]->t;
3195                    timefunc ( &ot, &mytimeinfo );
3196                    strftime( timestr, 80,"%Y.%m.%d %H:%M:%S", &mytimeinfo );
3197                    if ( arcsm->fromArc ) {
3198                        fprintf(htmlfile, "<tr bgcolor=\"DDDDFF\" class=\"WaveTableTextRowClass\"><td>%s%s : "
3199                                "%3s.%2s.%2s Distance=%5.1f km",
3200                                font_open,
3201                                waveSite->name, waveSite->comp,
3202                                waveSite->net, waveSite->loc,
3203                                distances[i]);
3204
3205                        // Include coda magnitudes, if available
3206                        if( coda_mags[i] != 0.0 && DontShowMd == 0)
3207                        {
3208                            fprintf( htmlfile, " <b>Md=%3.1f wt=%d</b>", coda_mags[i], coda_weights[i] );
3209                        }
3210
3211                        // Create the table row with the chart request
3212                        // Included font-size mandatory style for composite images
3213                        fprintf( htmlfile, "%s</td></tr>\n", font_close );
3214                    }
3215                    fprintf( htmlfile, "<tr class=\"WaveTableTraceRowClass\" style=\"font-size:0px;\"><td>%s%s%s</td></tr>\n",
3216                             font_open, chartreq, font_close);
3217                } // End of trace for cycle
3218
3219                /* Last boundary and close gif header file, if that is the case */
3220                if( UseGIF && !UseBlat )
3221                {
3222                    fprintf( gifHeader, "--FILEBOUNDARY--\n" );
3223                    fclose( gifHeader );
3224                }
3225
3226                // Free buffer
3227                free( buffer );
3228
3229                // Finish trace table
3230                fprintf( htmlfile, "</tbody>\n" );
3231                fprintf(htmlfile, "</table><br/><br/>\n");
3232
3233            } // End of Traces section
3234       
3235        }
3236
3237        /* Footer for html file
3238         ******************/
3239        if ( arcsm->fromArc && EarthquakeDisclaimer )
3240            fprintf(htmlfile,"<hr><div>\n%s\n</div><hr>\n",EarthquakeDisclaimer);
3241        else if ( !arcsm->fromArc && TriggerDisclaimer )
3242            fprintf(htmlfile,"<hr><div>\n%s\n</div><hr>\n",TriggerDisclaimer);
3243        fprintf(htmlfile, "<p id=\"Footer\">%sThis report brought to you by Earthworm with gmewhtmlemail (version %s)%s</p>", font_open, VERSION_STR, font_close);
3244
3245        /* Finish html file
3246         ******************/
3247        if ( Debug ) logit( "ot", "Closing file\n" );
3248        fprintf(htmlfile,"</body>\n</html>\n");
3249        fclose(htmlfile);
3250
3251        /* Send email
3252         *****************/
3253        logit("ot","nsites = %d, Max measured pga = %lg (cm/s/s), Max estimated pga = %lg (cm/s/s), threshold = %lg (cm/s/s)\n", 
3254                nsites, max_pga, max_epga_g*CM_S_S_1_g, PGAThreshold );
3255        /* note minquality check is inverted because A, B, C, D in chars A has lower value than B and so on */
3256        if ( strlen( EmailProgram ) <= 0 ) {
3257            /* No email program, so can't send email */
3258                logit("et", "gmewhtmlemail: No emails sent, No EmailProgram listed for sending.\n");
3259/* REMOVE this next check eventually??, because there is no Quality assignement from PDL.....*/
3260        } else if (Quality>MinQuality) {
3261                logit("et", "gmewhtmlemail: No emails sent, quality %c is below MinQuailty %c.\n",
3262                      Quality, MinQuality);
3263        } else if ( (!( (max_pga >= PGAThreshold) || 
3264                  (max_epga_g > 0.0 && max_epga_g*CM_S_S_1_g >= PGAThreshold) )) && ignore_PGA_threshold == 0) {
3265                logit("et", "gmewhtmlemail: No emails sent, no measured %f or estimated PGA %f above %lg (cm/s/s).\n",
3266                      max_pga, max_epga_g*CM_S_S_1_g, PGAThreshold);
3267        } else {
3268            // Check subscription list; if newer than what we have, reload subscriptions
3269            updateSubscriptions();       
3270           
3271            if ( nemailrecipients > 0 ) {
3272                double distance_from_center, azm;
3273                geo_to_km(center_lat, center_lon, arc->sum.lat, arc->sum.lon, &distance_from_center, &azm);
3274                logit( "ot", "gmewhtmlemail: processing  email alerts.\n" );
3275                for( i=0; i<nemailrecipients; i++ )// One email for each recipient
3276                {
3277                    if ( !shouldSendEmail( i, distance_from_center, arc, arcsm, subRegionsUsed, subAreasUsed ) ) {
3278                        //save this status so we know which addresses will be getting email for this event
3279                        emailrecipients[i].shouldSendEmail = 0;
3280                        continue;
3281                    } else emailrecipients[i].shouldSendEmail = 1;
3282                    /* Send it! */
3283                    if( UseBlat )       // Use blat for sending email
3284                    {
3285                        if( kml_filename[0] == 0 )
3286                        {
3287                            snprintf(system_command, 1024, "%s %s -html -to %s -subject \"%s "
3288                                    "- EW Event ID: %ld%s\" %s",
3289                                    EmailProgram, fullFilename, emailrecipients[i].address,
3290                                    SubjectPrefix, arc->sum.qid, regionsStr, BlatOptions);
3291                        }
3292                        else
3293                        {
3294                            /* the latest version of blat supports -attacht option for text file attachment, send the KML file */
3295                            snprintf(system_command, 1024, "%s %s -html -to %s -subject \"%s "
3296                                    "- EW Event ID: %ld%s\" -attacht %s %s",
3297                                    EmailProgram, fullFilename, emailrecipients[i].address,
3298                                    SubjectPrefix, arc->sum.qid, regionsStr, kml_filename, BlatOptions);
3299                        }
3300                    }
3301                    else                // Use sendmail
3302                    {
3303                        /* Create email header file
3304                         **************************/
3305                        snprintf( hdrFilename, 250, "%s_header.tmp", HTMLFile );
3306                        header_file = fopen( hdrFilename, "w" );
3307                        fprintf( header_file, "To: %s\n", emailrecipients[i].address );
3308                        if ( arcsm->fromArc )
3309                            fprintf( header_file, "Subject: %s - EW Event (%s) NEIC ID: %s%s\n",
3310                                     SubjectPrefix, timestrUTC, neic_id, regionsStr );
3311                        else
3312                            fprintf( header_file, "Subject: %s - Trigger from %s%s\n",
3313                                     SubjectPrefix, arcsm->sncl, regionsStr );
3314                        if( UseGIF )
3315                        {
3316                            /* When using GIFs the header must be different */
3317                            fprintf( header_file, "MIME-Version: 1.0\n"
3318                                     "Content-Type: multipart/mixed; boundary=\"FILEBOUNDARY\"\n\n"
3319                                     "--FILEBOUNDARY\n"
3320                                     "Content-Type: text/html\n\n" );
3321                            fclose( header_file );
3322
3323                            /* System command for sendmail with attachments */
3324                            snprintf(system_command, 1024, "cat %s %s %s | %s -t ",
3325                                    hdrFilename, fullFilename, gifHeaderFileName, EmailProgram);
3326                        }
3327                        else
3328                        {
3329                            fprintf( header_file, "Content-Type: text/html\n\n" );
3330                            fclose( header_file );
3331
3332                            /* System command for sendmail without attachments */
3333                            snprintf(system_command, 1024, "cat %s %s | %s -t ",
3334                                    hdrFilename, fullFilename, EmailProgram);
3335                        }
3336                    }
3337
3338                    /* Execute system command to send email
3339                     **************************************/
3340                    //printf( "Email command: '%s'\n", system_command );
3341                    system(system_command);
3342                    logit("et", "gmewhtmlemail: email sent to %s, passed tests\n", emailrecipients[i].address);
3343                    if (DebugEmail) {
3344                        logit("et", "gmewhtmlemail: Debug; EmailCommand issued '%s'\n", system_command);
3345                    }
3346                 }
3347               //only do this for events that we got from PDL, which we check by seeing that there's an NEIC id
3348               if (neic_id[0]) {
3349                  //set up file name
3350                  snprintf(eventAddrName, 250, "%s/%s", PathToEventEmail, neic_id);
3351                  //open "event address file"
3352                  if((event_address_file = fopen(eventAddrName, "w+")) == NULL) {
3353                     //this is an error and errno needs to be checked!!
3354                     logit("et", "gmewhtmlemail: Error; failed to open file %s to log email addresses!\n", eventAddrName);
3355                  } else {
3356                     //loop over email recipients again, saving which recipients were emailed for this event
3357                     for (i = 0; i < nemailrecipients; i++) {
3358                        if(emailrecipients[i].shouldSendEmail) {
3359                           fprintf(event_address_file, "%s\n", emailrecipients[i].address);
3360                        }
3361                     }
3362                     //close the file:
3363                     if(fclose(event_address_file)) {
3364                        //error closing the file!!! should be logged               
3365                        logit("et", "gmewhtmlemail: error closing file %s!\n", eventAddrName); 
3366                     } else {
3367                        if (DebugEmail) logit("et", "gmewhtmlemail: logged event recipients in file %s\n", eventAddrName);
3368                     }
3369                  }
3370               }
3371            }
3372        }
3373    } else {
3374        logit("et", "gmewhtmlemail: Unable to write html file: %s\n", fullFilename);
3375        rv = FALSE;
3376    }
3377    logit("et", "gmewhtmlemail: Completed processing of event id: %ld\n", arc->sum.qid);
3378    return rv;
3379}
3380
3381
3382
3383/*******************************************************************************
3384 * MapRequest: Produce a google map request for a given set of stations        *
3385 *                   and a hypocenter                                          *
3386 ******************************************************************************/
3387void MapRequest(char *request, SITE **sites, int nsites,
3388                double hypLat, double hypLon, HypoArcSM2 *arcsm,
3389                SiteOrder *site_order, SiteOrder *snl_order ) 
3390{
3391    if ( strlen(MapQuestKey) > 0 )
3392        MapQuestMapRequest( request, sites, nsites, hypLat, hypLon, arcsm, site_order, snl_order );
3393    else
3394        GoogleMapRequest( request, sites, nsites, hypLat, hypLon, arcsm, site_order, snl_order );
3395}
3396
3397/*******************************************************************************
3398 * GoogleMapRequest: Produce a google map request for a given set of stations  *
3399 *                   and a hypocenter                                          *
3400 ******************************************************************************/
3401void GoogleMapRequest(char *request, SITE **sites, int nsites,
3402                      double hypLat, double hypLon, HypoArcSM2 *arcsm,
3403                      SiteOrder *site_order, SiteOrder *snl_order ) 
3404{
3405    int i;
3406    int cap;
3407    char temp[MAX_GET_CHAR];
3408
3409    /* Base of the google static map
3410    *******************************/
3411    snprintf(request, MAX_GET_CHAR, "<img class=\"MapClass\" alt=\"\" "
3412             "src=\"https://maps.googleapis.com/maps/api/staticmap?"
3413             "size=600x400&format=png8&maptype=%s&sensor=false", StaticMapType);
3414
3415    /* Icon for hypocenter
3416    *********************/
3417    if (hypLat!=0.0 || hypLon!=0.0)
3418    {
3419        snprintf( temp, MAX_GET_CHAR, "%s&markers=icon:http:%%2F%%2Fmaps.google.com%%2Fmapfiles"
3420                  "%%2Fkml%%2Fpaddle%%2Fgrn-stars-lv.png%%7Cshadow:true%%7C%f,%f",
3421                  request, hypLat, hypLon);
3422        snprintf( request, MAX_GET_CHAR, "%s", temp );
3423    }
3424
3425    /* Add icons for stations
3426    ************************/
3427    snprintf( temp, MAX_GET_CHAR, "%s&markers=color:green%%7Cshadow:false", request );
3428    snprintf( request, MAX_GET_CHAR, "%s", temp );
3429    for( i = 0; i < nsites; i++ )
3430    {
3431        snprintf( temp, MAX_GET_CHAR, "%s%%7C%f,%f", request, sites[i]->lat, sites[i]->lon );
3432        snprintf( request, MAX_GET_CHAR, "%s", temp );
3433    }
3434
3435    /* Add icons for facilities
3436    **************************/
3437    cap = dam_close_count>damMapLimit ? damMapLimit : dam_close_count;
3438    for ( i=0; i<cap && i<26; i++ )
3439    {
3440        snprintf( temp, MAX_GET_CHAR, "%s&markers=icon:http:%%2F%%2Fmaps.google.com%%2Fmapfiles"
3441                  "%%2Fkml%%2Fpaddle%%2F%c.png%%7Cshadow:true%%7C%f,%f",
3442                  request, i+'A', dam_order[i]->lat, dam_order[i]->lon);
3443        snprintf( request, MAX_GET_CHAR, "%s", temp );
3444    }
3445
3446    for (i=0 ; i<cap; i++ )
3447    {
3448        snprintf( temp, MAX_GET_CHAR, "%s&markers=icon:http:%%2F%%2Fmaps.google.com%%2Fmapfiles"
3449                  "%%2Fkml%%2Fpaddle%%2Fred-circle.png%%7Cshadow:true%%7C%f,%f",
3450                  request, dam_order[i]->lat, dam_order[i]->lon);
3451        snprintf( request, MAX_GET_CHAR, "%s", temp );
3452    }
3453    /* End of the request
3454    ********************/
3455    snprintf( temp, MAX_GET_CHAR, "%s\"/>", request );
3456    snprintf( request, MAX_GET_CHAR, "%s", temp );
3457
3458}
3459
3460
3461/*******************************************************************************
3462 * MapQuestMapRequest: Produce a MapQuest map request for a given set of       *
3463 *                   stations and a hypocenter                                 *
3464 ******************************************************************************/
3465void MapQuestMapRequest(char *request, SITE **sites, int nsites,
3466                        double hypLat, double hypLon, HypoArcSM2 *arcsm,
3467                        SiteOrder *site_order, SiteOrder *snl_order )
3468{
3469    int i;
3470    int ix;
3471    int snl_i;
3472    int cap;
3473
3474    if( Debug )
3475        logit( "t", "ewhtmlemail: mapquest map creation starts\n");
3476    /* Base of the google static map
3477    *******************************/
3478    snprintf(request, MAX_GET_CHAR, "<img alt=\"Center/Station/Facility Map\" "
3479             "src=\"http://www.mapquestapi.com/staticmap/v4/getmap?key=%s&declutter=true&"
3480             "size=600,400&imagetype=png&type=%s", MapQuestKey, MQStaticMapType);
3481    request += strlen( request );
3482
3483    /* Icon for hypocenter
3484    *********************/
3485    if (hypLat!=0.0 || hypLon!=0.0)
3486    {
3487        //snprintf( request, MAX_GET_CHAR, "yellow_1,%f,%f|",
3488        snprintf( request, MAX_GET_CHAR, "&xis=http://love.isti.com/~scott/LASSO/hypocenter.png,1,C,%f,%f",
3489                  hypLat, hypLon);
3490        request += strlen( request );
3491    }
3492
3493    snprintf( request, MAX_GET_CHAR, "&pois=" );
3494    request += strlen( request );
3495
3496    /* Add icons for stations
3497    ************************/
3498
3499    if( Debug )
3500        logit( "t", "ewhtmlemail: adding %d sites\n", nsites);
3501    snl_i = 0;
3502    for( i = 0; i < nsites; i++ )
3503    {
3504        ix = site_order[i].idx;
3505
3506        if ( snl_order[snl_i].idx == ix ) {
3507            snprintf( request, MAX_GET_CHAR, "%spink_1-%d,%1.1f,%1.1f|", request, snl_i+1, sites[i]->lat, sites[i]->lon );
3508            request += strlen( request );
3509            snl_i++;
3510        }
3511    }
3512
3513    /* Add icons for facilities
3514    **************************/
3515    cap = dam_close_count>damMapLimit ? damMapLimit : dam_close_count;
3516    if( Debug )
3517        logit( "t", "ewhtmlemail: dam count to use %d \n", cap);
3518    for ( i=0; i<cap; i++ )
3519    {
3520        snprintf( request, MAX_GET_CHAR, "%s%s_1-%d,%1.1f,%1.1f|",
3521                  request, dam_order[i]->station[0] ? "blue" : "red",
3522                  i+1, dam_order[i]->lat, dam_order[i]->lon);
3523        request += strlen( request );
3524    }
3525    /* End of the map
3526    ********************/
3527    snprintf( request, MAX_GET_CHAR, "%s\"/>\n", request );
3528    request += strlen( request );
3529
3530    /* Add the legend
3531     ****************/
3532    snprintf( request, MAX_GET_CHAR, "<table border='1' cellspacing='1' cellpadding='3' width='600px'>"
3533              "<tr>"
3534              "<td><img src='http://love.isti.com/~scott/LASSO/hypocenter.png'> Event</td>"
3535              "<td><img src='http://www.mapquestapi.com/staticmap/geticon?uri=poi-red_1.png'> Uninstrumented Facility</td>"
3536              "<td><img src='http://www.mapquestapi.com/staticmap/geticon?uri=poi-blue_1.png'> Instrumented Facility</td>"
3537              "<td><img src='http://www.mapquestapi.com/staticmap/geticon?uri=poi-pink_1.png'> Station</td></tr>"
3538              "</table>" );
3539    request += strlen( request );
3540    if( Debug )
3541        logit( "t", "ewhtmlemail: mapquest map processing completed \n");
3542}
3543
3544/*******************************************************************
3545 * createGifPlot:
3546 *      plots data as GIF
3547 *      formats chart string to reference created GIF plot
3548 *
3549 *      returns 0 if successful, -1 otherwise
3550 *
3551 ******************************************************************/
3552int createGifPlot(char *chartreq, HypoArc *arc, char *buffer, int bsamplecount,
3553                                SITE *waveSite, double starttime, double endtime, char phaseName[2])
3554{
3555        int BACKCOLOR, TRACECOLOR;
3556        gdImagePtr gif;
3557        char giffilename[256];
3558        char contentID[256];
3559        FILE *giffile;
3560        unsigned char *gifbuffer, *base64buf;
3561        size_t gifbuflen, base64buflen;
3562
3563    /* Produce GIF image
3564     *******************/
3565    gif = gdImageCreate( TraceWidth, TraceHeight ); // Creates base image
3566
3567        // Set colors for the trace and pick
3568    BACKCOLOR = gdImageColorAllocate( gif, 255, 255, 255 ); // White
3569    TRACECOLOR = gdImageColorAllocate( gif, 0, 0, 96 );     // Dark blue
3570
3571    /* Plot trace
3572     ************/
3573    if( trbf2gif( buffer, bsamplecount, gif, starttime, endtime, TRACECOLOR, BACKCOLOR ) == 1 )
3574    {
3575        if( Debug ) logit( "o", "Created gif image for trace from %s.%s.%s.%s\n",
3576                                                waveSite->name, waveSite->comp, waveSite->net, waveSite->loc );
3577    }
3578    else
3579    {
3580        logit( "e", "gmewhtmlemail: Unable to create gif image for %s.%s.%s.%s\n",
3581                                                waveSite->name, waveSite->comp, waveSite->net, waveSite->loc );
3582        return -1;
3583    }
3584
3585    /* Open gif file */
3586    snprintf( giffilename, 256, "%s_%ld_%s.%s.%s.%s_%s.gif", HTMLFile, arc->sum.qid,
3587                                        waveSite->name, waveSite->comp, waveSite->net, waveSite->loc, phaseName );
3588    giffile = fopen( giffilename, "wb+" );
3589    if (giffile == NULL) {
3590        logit( "e", "gmewhtmlemail: Unable to create gif image for %s.%s.%s.%s as a file: %s\n",
3591                                                waveSite->name, waveSite->comp, waveSite->net, waveSite->loc, giffilename );
3592        return -1;
3593    }
3594
3595    /* Save gif to file */
3596    gdImageGif( gif, giffile );
3597
3598    if( !UseBlat )
3599    {
3600        /* The gif must be converted to base64 */
3601
3602        /* Rewind stream */
3603        rewind( giffile );
3604
3605        /* Allocate memory for reading from file */
3606        gifbuflen = ( size_t )( gif->sx * gif->sy );
3607        gifbuffer = ( unsigned char* ) malloc( sizeof( unsigned char ) * gifbuflen );
3608
3609        /* Read gif from file */
3610        gifbuflen = fread( gifbuffer, sizeof( unsigned char ), gifbuflen, giffile );
3611
3612        /* Encode to base64 */
3613        base64buf = base64_encode( (size_t *) &base64buflen, gifbuffer, gifbuflen );
3614        base64buf[base64buflen] = 0;
3615
3616        /* Free gif buffer */
3617        free( gifbuffer );
3618
3619        sprintf( chartreq,
3620                                                "<img class=\"MapClass\" alt=\"Waveform\" src=\"data:image/gif;base64,%s\">",
3621                         base64buf );
3622
3623        /* Free base64 buffer */
3624        free( base64buf );
3625    }
3626    else
3627    {
3628        //TODO: Investigate blat attachment references
3629        /* Create img request */
3630        sprintf( chartreq,
3631                                                "<img class=\"MapClass\" alt=\"\" src=\"cid:%s\">",
3632                        contentID );
3633    }
3634    /* Close gif file */
3635    fclose( giffile );
3636
3637    // free gif from memory
3638    gdImageDestroy(gif);
3639        return 0;
3640}
3641
3642/**********************************************************************************************
3643 * writePlotRequest:
3644 *      formats request to Flask EW plotter given a minimum of SNCL, starttime and duration
3645 *      of trace to be plotted
3646 *
3647 *      writes formatted request into the string buffer 'plotRequest'
3648 *      returns 0 if successful, -1 otherwise
3649 *********************************************************************************************/
3650int writePlotRequest(char *plotRequest, size_t requestLen, long eventId, char *address, char *port,
3651                                        char *network, char *station, char *location, char *channel,
3652                                        time_t startTime, int duration)
3653{
3654        struct tm *tPtr;
3655        char startTimeStr[20];
3656        char requestURL[250];           /* Buffer to hold request for plot from EWAVE */
3657        int formatStringLen = 0;
3658        char conversion_SNCL[20] = {0};
3659        double conversionFactor = 0.0;
3660        char unitLabel[20] = {0};
3661        int i;
3662
3663        tPtr = gmtime(&startTime);
3664        strftime(startTimeStr, 20, "%FT%T", tPtr);
3665
3666        if ((address == NULL) || (port == NULL)) {
3667                return -1;
3668        }
3669
3670        //Look up conversion factor:
3671        snprintf(conversion_SNCL, 20, "%s.%s.%s.%s", network, station, channel, location);
3672        if (Debug) logit( "et", "looking up conversion factor for %s\n", conversion_SNCL);
3673        for (i = 0; i<MAX_STREAMS && (strlen(ConversionFactors[i].SNCL) > 0); i++) {
3674                if ( strncmp(ConversionFactors[i].SNCL, conversion_SNCL, 20) == 0 ) {
3675                        conversionFactor = ConversionFactors[i].convFactor;
3676                        if (Debug) logit("et", "conversion factor found for %s: %lf\n", conversion_SNCL, conversionFactor);
3677                        break;
3678                }       
3679        }
3680
3681        if (conversionFactor == 0.0) {
3682                conversionFactor = 1.0;
3683                strcpy(unitLabel, "counts");
3684        } else {
3685                //transform factor in order to scale from counts to acceleration in cm/s^2
3686                conversionFactor = (10.0)/(conversionFactor);
3687                strcpy(unitLabel, "cm/s/s");   
3688        }
3689
3690        formatStringLen = snprintf(requestURL, 250,
3691                                        "http://%s:%s/ewave/query?net=%s&sta=%s&cha=%s&loc=%s&start=%s&dur=%d&displayMaxValue=True&scaleFactor=%.17g&units=%s",
3692                                        address, port, network, station, channel, location, startTimeStr, duration, conversionFactor, unitLabel);
3693        if ( formatStringLen <= 0 ) {
3694                if (Debug) logit( "et", "Unable to format request to EWAVE\n");
3695                return -1;
3696        }
3697        formatStringLen = snprintf(plotRequest, requestLen,
3698                                "<div style=\"float:left;overflow:hidden\">\n"
3699                                "\t<img alt=\"Trace\" title=\"Trace\" style=\"margin:0px -2px\""
3700                                "src=\"%s\"/>\n</div>\n", requestURL);
3701
3702        if ( formatStringLen <= 0 ) {
3703                if (Debug) logit( "et", "failed to write image file name\n");
3704                return -1;
3705        }
3706
3707        return 0;
3708}
3709
3710/*******************************************************************************
3711 * makeGoogleChart: Produce a google chart request for a given station  *
3712 ******************************************************************************/
3713/*
3714* Input Parameters
3715* chartreq:        String to save the request in
3716* samples:         Array with the samples
3717* samplecount:     Number of samples
3718* phaseName:       A character to indicate the name of the phase (P or S)
3719* phasePos:        The relative position of the phase label 0%-100%
3720* tracewidth:      Width of the google trace
3721* traceheight:     Height of the google trace
3722*/
3723int makeGoogleChart( char* chartreq, int *samples, int samplecount,
3724                     char * phasename, double phasepos, int tracewidth, int traceheight, double *maxmin )
3725{
3726    int i;
3727    double avg, max=0, min=0;
3728    char reqHeader[MAX_GET_CHAR];
3729    char reqSamples[MAX_GET_CHAR];
3730    int cursample;              /* Position of the current sample */
3731    int cursampcount;           /* Number of samples in the current image */
3732    int curimgwidth;            /* Width of the current image */
3733    int accimgwidth;            /* Accumulated width of the composite image */
3734    int ntraces;                /* Number of traces that will be required */
3735    double cursamppos, nxtsamppos;
3736    int max_index;
3737    int min_index;
3738    char maxMarker[100] = {0};
3739    char rangeMarker[100] = {0};
3740    double max_pct;
3741
3742
3743    // Average value of the samples
3744    avg = 0;
3745    for( i = 0; i < samplecount; i++ ) {
3746        if ( i==0 || max < samples[i] ) {
3747            max = samples[i];
3748            max_index = i;
3749        }
3750        if ( i==0 || min > samples[i] ) {
3751            min = samples[i];
3752            min_index = i;
3753        }
3754        avg += samples[i];
3755    }
3756    avg /= ( double ) samplecount;
3757    // fix for if max is negative swing....
3758    if (fabs(min-avg) > fabs(max-avg)) {
3759        max_index = min_index;
3760    }
3761
3762
3763    /* Instead of sending a single image, the trace will be split
3764     * in multiple images, each with its separate request and a
3765     * maximum of MAX_REQ_SAMPLES samples
3766     ************************************************************/
3767
3768    ntraces = ( ( int )( samplecount %  MAX_REQ_SAMP ) == 0 )?
3769              ( ( int )( samplecount / MAX_REQ_SAMP ) ) :
3770              ( ( int )( samplecount / MAX_REQ_SAMP ) + 1 );
3771    if( Debug )
3772        logit( "o", "ewhtmlemail: Splitting trace in %d images\n", ntraces );
3773
3774    cursample = 0;
3775    accimgwidth = 0;
3776    chartreq[0] = '\0';
3777    while( cursample < samplecount )
3778    {
3779
3780        /* Determine how many samples will be used in this image
3781         *******************************************************/
3782        cursampcount = ( ( cursample + MAX_REQ_SAMP ) > samplecount )?
3783                       ( samplecount - cursample ) : MAX_REQ_SAMP;
3784
3785
3786        /* Compute width of the image
3787         ****************************/
3788        curimgwidth = ( int )( ( double )( cursample + cursampcount ) /
3789                               ( double ) samplecount * ( double ) tracewidth ) - accimgwidth;
3790        accimgwidth += curimgwidth;     // update accumulated image width
3791
3792
3793        /* Determine range markers
3794         ***********************/
3795        if ( cursample==0 || cursample+cursampcount >= samplecount ) {
3796            if ( cursample==0 && cursample+cursampcount >= samplecount ) 
3797                sprintf( rangeMarker, "chm=@t%1.2f,000000,0,0:1,10|@t%1.2f,000000,0,0:0,10|@t%1.2f,000000,0,1:1,10|@t%1.2f,000000,0,1:0,10&", maxmin[0], maxmin[1], maxmin[0], maxmin[1] );
3798            else
3799                sprintf( rangeMarker, "chm=@t%1.2f,000000,0,%d:1,10|@t%1.2f,000000,0,%d:0,10&", maxmin[0], cursample==0 ? 0 : 1, maxmin[1], cursample==0 ? 0 : 1 );
3800        } else {
3801            rangeMarker[0] = 0;
3802        }
3803
3804        /* Determine background
3805         ***********************/
3806        if ( !showMax )
3807            maxMarker[0] = 0;
3808        else if ( max_index < cursample ) {
3809            // Max is left of this segment => background is <AFTER_MAX_COLOR>
3810            sprintf( maxMarker, "chf=bg,ls,0,%s&", AFTER_MAX_COLOR );
3811        } else if ( max_index >= cursample+cursampcount ) {
3812            // Max is right of this segment => background is <BEFORE_MAX_COLOR>
3813            sprintf( maxMarker, "chf=bg,ls,0,%s&", BEFORE_MAX_COLOR );
3814        } else {
3815            // Max is within this segment => background is split
3816            max_pct = (max_index - cursample) / (1.0 * cursampcount);
3817            sprintf( maxMarker, "chf=bg,ls,0,%s,%f,%s,%f&", 
3818                BEFORE_MAX_COLOR, max_pct, AFTER_MAX_COLOR, 1.0);
3819        }
3820
3821        /* Create img request with cursampcount samples
3822         *************************************************/
3823        cursamppos = ( double ) cursample / ( double ) samplecount * 100.0;
3824        nxtsamppos = ( double )( cursample + cursampcount ) /
3825                     ( double ) samplecount * 100.0;
3826        if( phasepos >= cursamppos && phasepos < nxtsamppos )
3827        {
3828            /* Get image with phase label
3829             ****************************/
3830            snprintf( reqHeader, MAX_GET_CHAR,
3831                      "<div style=\"float:left;overflow:hidden\">\n"
3832                      "\t<img alt=\"\" style=\"margin:0px -2px\""
3833                      "src=\"http://chart.apis.google.com/chart?"
3834                      "chs=%dx%d&"
3835                      "cht=ls&"
3836                      "chxt=x&"
3837                      "chxl=0:|%s&"
3838                      "chxp=0,%3.1f&"
3839                      "chxtc=0,-600&"
3840                      "chco=000060&"
3841                      "chma=0,0,0,0&"
3842                      "chls=1&"               // Linewidth
3843                      "%s"                    // Place for Max marker: chf=bg,ls,0,F0F0F0,0.883333,FFFFFF,0.116667 or chf=bg,s,F0F0F0
3844                      "%s"                    // Place for Range Labels, if first or last subplot
3845                      "chd=s:",
3846                      curimgwidth + 4, traceheight,
3847                      phasename, 
3848                      ( phasepos - cursamppos ) / ( nxtsamppos - cursamppos ) * 100.0,
3849                      maxMarker, rangeMarker );
3850        }
3851        else
3852        {
3853            /* Get image with no label
3854             *************************/
3855            snprintf( reqHeader, MAX_GET_CHAR,
3856                      "<div style=\"float:left;overflow:hidden\">\n"
3857                      "\t<img alt=\"\" style=\"margin:0px -2px\""
3858                      "src=\"http://chart.apis.google.com/chart?"
3859                      "chs=%dx%d&"
3860                      "cht=ls&"
3861                      "chxt=x&"
3862                      "chxl=0:|&"
3863                      "chxp=0,&"
3864                      "chxtc=0,-600&"
3865                      "chco=000060,006000&"
3866                      "chma=0,0,0,0|0,006000,1,0&"
3867                      "chls=1|1&"               // Linewidth
3868                      "%s"                    // Place for Max marker: chf=bg,ls,0,F0F0F0,0.883333,FFFFFF,0.116667 or chf=bg,s,F0F0F0
3869                      "%s"                    // Place for Range Labels, if first or last subplot
3870                      "chd=s:",
3871                      curimgwidth + 4, traceheight,
3872                      maxMarker, rangeMarker);
3873        }
3874
3875        /* Create samples
3876         ****************/
3877        for( i = 0; i < MAX_GET_CHAR; i++ ) reqSamples[i] = 0;
3878        for( i = cursample; i < ( cursample + cursampcount ); i++ ) {
3879              reqSamples[i - cursample] = simpleEncode( ( int )( ( double ) samples[i] - avg + 30.5 ) );
3880        }
3881        cursample += cursampcount;
3882
3883        /* Add image and samples to final request
3884         ****************************************/
3885        strncat( chartreq, reqHeader, MAX_GET_CHAR );
3886        strncat( chartreq, reqSamples, MAX_GET_CHAR );
3887
3888
3889        /* Terminate img tag
3890         *******************/
3891        strncat( chartreq, "\"/>\n</div>\n", MAX_GET_CHAR );
3892
3893    }
3894
3895    return 1;
3896}
3897
3898
3899/*******************************************************************************
3900 * simpleEncode: This is a simple integer encoder based on googles function    *
3901 *******************************************************************************/
3902char simpleEncode(int i) {
3903    char base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
3904
3905    /* truncate input
3906    ****************/
3907    if (i<0)
3908        i=0;
3909    if (i>61)
3910        i=61;
3911
3912
3913    return base[i];
3914}
3915
3916
3917/*******************************************************************************
3918 * searchSite: To replace site index, which does not seem to work well         *
3919 *******************************************************************************/
3920int searchSite(char *S, char *C, char *N, char *L) {
3921    int i;
3922
3923    for (i=0; i<nSite; i++)
3924    {
3925        if (
3926            strcmp(S,Site[i].name)==0 &&
3927            strcmp(C,Site[i].comp)==0 &&
3928            strcmp(N,Site[i].net)==0 &&
3929            strcmp(L,Site[i].loc)==0 )
3930            return i;
3931    }
3932
3933    return -1;
3934}
3935
3936
3937/****************************************************************************************
3938 * getWStrbf: Retrieve a set of samples from the waveserver and store the raw tracebuf2 *
3939 *            in a buffer                                                               *
3940 ****************************************************************************************/
3941int getWStrbf( char *buffer, int *buflen, SITE *site,
3942               double starttime, double endtime )
3943{
3944    WS_MENU_QUEUE_REC   menu_queue;
3945    TRACE_REQ           trace_req;
3946    int                 wsResponse;
3947    int                 atLeastOne;
3948    int                 i;
3949    char                WSErrorMsg[80];
3950   
3951    /* Initialize menu queue
3952     ***********************/
3953    menu_queue.head = NULL;
3954    menu_queue.tail = NULL;
3955
3956    atLeastOne = 0;
3957
3958    /* Make menu request
3959    *******************/
3960    for( i = 0; i < nwaveservers; i++ )
3961    {
3962        wsResponse = wsAppendMenu(
3963                         waveservers[i].wsIP, waveservers[i].port,
3964                         &menu_queue, wstimeout );
3965        if( wsResponse != WS_ERR_NONE )
3966        {
3967            logit( "et", "gmewhtmlemail: Cannot contact waveserver %s:%s - %d ",
3968                   waveservers[i].wsIP, waveservers[i].port, wsResponse );
3969            logit( "et", "%s\n",
3970                   getWSErrorStr(wsResponse, WSErrorMsg ) );
3971            continue;
3972        }
3973        else
3974        {
3975            atLeastOne++;
3976        }
3977    }
3978    if( atLeastOne == 0 )
3979    {
3980        logit( "et", "gmewhtmlemail: Unable to contact any waveserver.\n");
3981        return -1;
3982    }
3983
3984
3985    /* Make request structure
3986    ************************/
3987    strcpy( trace_req.sta, site->name );
3988    strcpy( trace_req.chan, site->comp );
3989    strcpy( trace_req.net, site->net );
3990    strcpy( trace_req.loc, site->loc );
3991    trace_req.reqStarttime = starttime;
3992    trace_req.reqEndtime = endtime;
3993    trace_req.partial = 1;
3994    trace_req.pBuf = buffer;
3995    trace_req.bufLen = *buflen;
3996    trace_req.timeout = wstimeout;
3997    trace_req.fill = 0;
3998
3999
4000    /* Pull data from ws
4001     *******************/
4002    wsResponse = wsGetTraceBinL( &trace_req, &menu_queue, wstimeout );
4003    if( wsResponse != WS_ERR_NONE )
4004    {
4005        logit( "et", "gmewhtmlemail: Error loading data from waveserver - %s\n",
4006               getWSErrorStr(wsResponse, WSErrorMsg));
4007        wsKillMenu(&menu_queue);
4008        return -1;
4009    }
4010
4011    /* Update output number of bytes
4012     *********************************/
4013    *buflen = (int)trace_req.actLen;
4014
4015
4016    /* Terminate connection
4017     **********************/
4018    wsKillMenu(&menu_queue);
4019
4020    return 1;
4021}
4022
4023
4024char* getWSErrorStr(int errorCode, char* msg) {
4025    switch (errorCode)
4026    {
4027    case 1:
4028        strcpy(msg,"Reply flagged by server");
4029        return msg;
4030    case -1:
4031        strcpy(msg,"Faulty or missing input");
4032        return msg;
4033    case -2:
4034        strcpy(msg,"Unexpected empty menu");
4035        return msg;
4036    case -3:
4037        strcpy(msg,"Server should have been in menu");
4038        return msg;
4039    case -4:
4040        strcpy(msg,"SCNL not found in menu");
4041        return msg;
4042    case -5:
4043        strcpy(msg,"Reply truncated at buffer limit");
4044        return msg;
4045    case -6:
4046        strcpy(msg,"Couldn't allocate memory");
4047        return msg;
4048    case -7:
4049        strcpy(msg,"Couldn't parse server's reply");
4050        return msg;
4051    case -10:
4052        strcpy(msg,"Socket transaction timed out");
4053        return msg;
4054    case -11:
4055        strcpy(msg,"An open connection was broken");
4056        return msg;
4057    case -12:
4058        strcpy(msg,"Problem setting up socket");
4059        return msg;
4060    case -13:
4061        strcpy(msg,"Could not make connection");
4062        return msg;
4063    }
4064    //Unknown error
4065    return NULL;
4066}
4067
4068
4069
4070
4071
4072
4073/****************************************************************************************
4074 * trbufresample: Resample a trace buffer to a desired sample rate. Uses squared low    *
4075 *            filtering and oversampling                                                *
4076 ****************************************************************************************/
4077int trbufresample( int *outarray, int noutsig, char *buffer, int buflen,
4078                   double starttime, double endtime, int outamp, double *maxmin )
4079{
4080    TRACE2_HEADER   *trace_header,*trace;       // Pointer to the header of the current trace
4081    char            *trptr;             // Pointer to the current sample
4082    double          inrate;             // Initial sampling rate
4083    double          outrate;            // Output rate
4084    int             i;                  // General purpose counter
4085    double          s;                  // Sample
4086    double          *fbuffer;           // Filter buffer
4087    double          nfbuffer;           // length of the filter buffer
4088    double          *outsig;            // Temporary array for output signal
4089    double          outavg;             // Compute average of output signal
4090    double          outmax;             // Compute maximum of output signal
4091    double          *samples;           // Array of samples to process
4092    int             nsamples = 0;       // Number of samples to process
4093    int             samppos = 0;        // Position of the sample in the array;
4094    double          sr;                     // buffer sample rate
4095    int             isBroadband;        // Broadband=TRUE - ShortPeriod = FALSE
4096    int             flag;
4097    double          y;              // temp int to hold the filtered sample
4098    RECURSIVE_FILTER rfilter;     // recursive filter structure
4099    int             maxfilters=20;
4100    int             retval;
4101    double          realMax, realMin;
4102    int             minMaxStarted = 0;
4103
4104
4105    /* Reserve memory for temporary output signal
4106     ********************************************/
4107    outsig = ( double* ) malloc( sizeof( double ) * noutsig );
4108    if( outsig == NULL )
4109    {
4110        logit( "et", "gmewhtmlemail: Unable to reserve memory for resampled signal\n" );
4111        return -1;
4112    }
4113
4114
4115    /* Reserve memory for input samples
4116     **********************************/
4117    // RSL note: tried the serialized option but this is better
4118    samples = ( double* ) malloc( sizeof( double ) * MAX_SAMPLES );
4119    if( samples == NULL )
4120    {
4121        logit( "et", "gmewhtmlemail: Unable to allocate memory for sample buffer\n" );
4122        return -1;
4123    }
4124    for( i = 0; i < MAX_SAMPLES; i++ ) samples[i] = 0.0;
4125
4126    /* Isolate input samples in a single buffer
4127     ******************************************/
4128    trace_header = (TRACE2_HEADER*)buffer;
4129    trptr = buffer;
4130    outavg = 0; // Used to initialize the filter
4131    if(SPfilter)
4132    {
4133        trace = (TRACE2_HEADER*)buffer;
4134        sr =trace->samprate; /*passing the sample rate from buffer header*/
4135        /*check if this channel is a broadband one or SP
4136         * This simply check out the first two channel characters in order to conclude
4137         * whether it is a broadband (BH) or shorperiod (SP)*/
4138        switch(trace->chan[0])
4139        {
4140        case 'B':
4141            isBroadband = TRUE;
4142            break;
4143        case 'H':
4144            isBroadband = TRUE;
4145            break;
4146        default:
4147            isBroadband = FALSE;
4148            break;
4149        }
4150    }
4151    while( ( trace_header = ( TRACE2_HEADER* ) trptr ) < (TRACE2_HEADER*)(buffer + buflen) )
4152    {
4153        // If necessary, swap bytes in tracebuf message
4154        retval = WaveMsg2MakeLocal( trace_header );
4155        if ( retval < 0 )
4156        {
4157            logit( "et", "gmewhtmlemail(trbufresample): WaveMsg2MakeLocal error. (%d)\n", retval );
4158            return -1;
4159        }
4160
4161        // Update sample rate
4162        inrate = trace_header->samprate;
4163
4164        // Skip the trace header
4165        trptr += sizeof( TRACE2_HEADER );
4166
4167        for( i = 0; i < trace_header->nsamp; i++ )
4168        {
4169            // Produce integer sample
4170            if( strcmp( trace_header->datatype, "i2" ) == 0 ||
4171                    strcmp(trace_header->datatype, "s2")==0 )
4172            {
4173                s = ( double ) (*((short*)(trptr)));
4174                trptr += 2;
4175            }
4176            else
4177            {
4178                s = ( double )( *( ( int* )( trptr ) ) );
4179                trptr += 4;
4180            }
4181            if ( minMaxStarted ) {
4182                if ( s < realMin )
4183                    realMin = s;
4184                if ( s > realMax )
4185                    realMax = s;
4186            } else {
4187                realMax = realMin = s;
4188                minMaxStarted = 1;
4189            }
4190
4191            // Compute position of the sample in the sample array
4192            samppos = ( int )( ( trace_header->starttime
4193                                 + ( double ) i / trace_header->samprate - starttime ) *
4194                               trace_header->samprate );
4195
4196            if( samppos < 0 || samppos >= MAX_SAMPLES )
4197                continue;
4198
4199            // Store sample in array
4200            samples[samppos] = s;
4201            if( samppos > nsamples ) nsamples = samppos;
4202            outavg += s;
4203        }
4204    }
4205    maxmin[0] = realMax;
4206    maxmin[1] = realMin;
4207
4208    if(SPfilter && isBroadband)
4209    {
4210        if( (flag=initAllFilters(maxfilters)) != EW_SUCCESS)
4211        {
4212            logit("et","gmewhtmlemail: initAllFilters() cannot allocate Filters; exiting.\n");
4213        }
4214        if( (flag=initTransferFn()) != EW_SUCCESS)
4215        {
4216            logit("et","gmewhtmlemail: initTransferFn() Could not allocate filterTF.\n");
4217        }
4218        if(flag==EW_SUCCESS)
4219        {
4220            switch (flag=initChannelFilter(sr, outavg/(double)nsamples,isBroadband,&rfilter, maxfilters))
4221            {
4222            case  EW_SUCCESS:
4223
4224                if (Debug)
4225                    logit("et","gmewhtmlemail: Pre-filter ready for channel %s:%s:%s:%s\n",
4226                          trace->sta,trace->chan,trace->net,trace->loc);
4227                break;
4228
4229            case EW_WARNING:
4230
4231                logit("et","Unable to obtain a find an existing pre-filter or create a new (%s) pre-filter for sample rate %f; setting channel %s:%s:%s:%s bad\n",
4232                      (isBroadband ? "broadband" : "narrowband"),trace->samprate,
4233                      trace->sta,trace->chan,trace->net,trace->loc);
4234                break;
4235
4236            case EW_FAILURE:
4237
4238                logit("et",  "gmewhtmlemail Parameter passed in NULL; setting channel %s:%s:%s:%s bad\n",
4239                      trace->sta,trace->chan,trace->net,trace->loc);
4240                break;
4241
4242            default:
4243
4244                logit("et","Unknown error in obtaining a pre-filter for sample rate %f; setting channel %s:%s:%s:%s bad\n",
4245                      trace->samprate, trace->sta,trace->chan,trace->net,trace->loc);
4246                break;
4247            }
4248        }
4249        if( flag == EW_SUCCESS )
4250        {   /*The channel filters function went ok*/
4251
4252            outavg = 0;
4253            for(i=0; i<nsamples; i++) {
4254                y=filterTimeSeriesSample(samples[i],&rfilter);
4255                samples[i]=y;
4256                outavg += (double)samples[i];
4257            }
4258            // Remove average from samples and normalize
4259            outavg /= (double)nsamples;
4260            /*reset the transfer function*/
4261            freeTransferFn();
4262        }
4263        else
4264        {
4265            /*Something was wrong and it is not possible to filter the samples*/
4266            outavg /= nsamples;//move this part
4267        }
4268
4269    }
4270    else
4271    {   /*it was not selected SPfilter*/
4272        outavg /= nsamples;
4273    }
4274
4275    /* Prepare filter buffer
4276     ***********************/
4277    outrate = ( double ) noutsig / ( endtime - starttime ); // Output sample rate
4278    nfbuffer = inrate / outrate; // Length of the filter buffer
4279
4280    /* Check if filtering is required
4281     ********************************/
4282    if( nfbuffer > 1.5 )
4283    {
4284        int nb = ( int )( nfbuffer + 0.5 );
4285        if( Debug ) logit( "o", "Filtering is required\nFilter settings:  "
4286                               "Input rate: %f    Output rate: %f    Buffer length: %f\n",
4287                               inrate, outrate, nfbuffer );
4288        fbuffer = ( double* ) malloc( sizeof( double ) * nb );
4289        if( fbuffer == NULL )
4290        {
4291            logit( "et", "gmewhtmlemail: Unable to allocate memory for filter buffer\n" );
4292            return -1;
4293        }
4294        for( i = 0; i < nb; i++ )
4295            fbuffer[i] = outavg; // Initialize filter with average of signal
4296
4297        /* Filter signal with array buffer
4298         *********************************/
4299        for( i = 0; i < nsamples; i++ )
4300            samples[i] = fbuffer_add( fbuffer, nb, samples[i] );
4301
4302        /* Done with filter buffer
4303         *************************/
4304        free( fbuffer );
4305    }
4306    else
4307    {
4308        if( Debug ) logit( "o", "Filtering is not required.\n" );
4309    }
4310
4311        /* Resampling
4312     * This will be based on finding the sample to place
4313     * at each point of the output array. Allows also upsampling
4314     * if necessary
4315     ***********************************************************/
4316    if( Debug ) logit( "o", "Resampling at a rate of %f\n",
4317                           inrate / outrate );
4318    outavg = 0;
4319    for( i = 0; i < noutsig; i++ )
4320    {
4321        /* for the i-th sample of the output signal, calculate
4322         * the position at the input array
4323         *****************************************************/
4324        samppos = ( int )( ( double ) i * inrate / outrate + 0.5 );
4325
4326
4327        /* check if position is valid
4328         ****************************/
4329        if( samppos >= nsamples )
4330            continue; // Invalid position
4331
4332        /* store sample
4333         **************/
4334        outsig[i] = samples[samppos];
4335        outavg += outsig[i]; // update average
4336    }
4337    outavg /= noutsig;
4338
4339    /* Remove average of output signal
4340     *********************************/
4341    outmax = 0;
4342    for( i = 0; i < noutsig; i++ )
4343    {
4344        // only remove the average if the data are not in a gap (value = 0.0)
4345        //                 // otherwise we get the dreaded honeycomb effect
4346        if (outsig[i] != 0.0)
4347        {
4348              outsig[i] = outsig[i] - outavg; // remove average
4349        }
4350
4351        // Compute maximum value
4352        if( outsig[i] > outmax ) outmax = outsig[i];
4353        if( outsig[i] < -outmax ) outmax = -outsig[i];
4354    }
4355
4356    for(i = 0; i < noutsig; i++ )
4357    {
4358        outarray[i] = ( int ) ( outsig[i] / outmax * ( double ) outamp + 0.5 );
4359    }
4360
4361    // free memory
4362    free( outsig );
4363    free( samples );
4364    return 1;
4365
4366}
4367
4368/* Add a new sample to the buffer filter and compute output */
4369double fbuffer_add( double *fbuffer, int nfbuffer, double s )
4370{
4371    int i;
4372    double out = 0;
4373
4374    // circulate buffer
4375    for( i = 1; i < nfbuffer; i++ )
4376        fbuffer[i - 1] = fbuffer[i];
4377
4378    // add sample to buffer
4379    fbuffer[nfbuffer - 1] = s;
4380
4381    // compute filter output by averaging buffer
4382    for( i = 0; i < nfbuffer; i++ )
4383        out += fbuffer[i];
4384    out /= ( double ) nfbuffer;
4385
4386    return out;
4387}
4388
4389
4390
4391/****************************************************************************************
4392 * trb2gif: Takes a buffer with tracebuf2 messages and produces a gif image with the    *
4393 *          corresponding trace                                                         *
4394 ****************************************************************************************/
4395int trbf2gif( char *buf, int buflen, gdImagePtr gif, double starttime, double endtime,
4396              int fcolor, int bcolor )
4397{
4398    TRACE2_HEADER   *trace_header,*trace;   // Pointer to the header of the current trace
4399    char            *trptr;         // Pointer to the current sample
4400    double          sampavg;        // To compute average value of the samples
4401    int         nsampavg=0;     // Number of samples considered for the average
4402    double          sampmax;        // To compute maximum value
4403    int             i;              // General purpose counter
4404    gdPointPtr      pol;            // Polygon pointer
4405    int             npol;           // Number of samples in the polygon
4406    double          xscale;         // X-Axis scaling factor
4407    double          dt;             // Time interval between two consecutive samples
4408    double      sr;             // to hold the sample rate obtained through the trace_header struc.
4409    int             isBroadband;    // Broadband= TRUE - ShortPeriod = FALSE
4410    int             flag;
4411    int             y;              // temp int to hold the filtered sample
4412    RECURSIVE_FILTER rfilter;     // recursive filter structure
4413    int             maxfilters=20;      //Number of filters fixed to 20.
4414
4415    // Reserve memory for polygon points //TODO Make this dynamic
4416    pol = ( gdPointPtr ) malloc( MAX_POL_LEN * sizeof( gdPoint ) );
4417    if( pol == NULL )
4418    {
4419        logit( "et", "trbf2gif: Unable to reserve memory for plotting trace.\n" );
4420        return -1;
4421    }
4422    npol = 0;
4423
4424    // Initialize trace pointer
4425    trace_header = (TRACE2_HEADER*)buf;
4426    trptr = buf;
4427
4428    // Initialize maximum counter to minimum integer
4429    sampmax = 0;
4430    sampavg = 0;
4431
4432    // Compute x-axis scale factor in pixels/second
4433    xscale = ( double ) gif->sx / ( endtime - starttime );
4434
4435    // Cycle to process each trace an draw it on the gif image
4436    while( ( trace_header = ( TRACE2_HEADER* ) trptr ) < (TRACE2_HEADER*)(buf + buflen) )
4437    {
4438        // Compute time between consecutive samples
4439        dt = ( trace_header->endtime - trace_header->starttime ) /
4440             ( double ) trace_header->nsamp;
4441
4442        // If necessary, swap bytes in tracebuf message
4443        if ( WaveMsg2MakeLocal( trace_header ) < 0 )
4444        {
4445            logit( "et", "trbf2gif(trb2gif): WaveMsg2MakeLocal error.\n" );
4446            free( pol );
4447            return -1;
4448        }
4449
4450        // Store samples in polygon
4451        // This procedure saves all the samples of this trace on the polygon
4452        // It also stores the maximum value and average values of the samples
4453        // for correcting the polygon later
4454
4455        // Skip the trace header
4456        trptr += sizeof( TRACE2_HEADER );
4457        if(SPfilter)
4458        {
4459            trace = (TRACE2_HEADER*)buf;
4460            sr = trace->samprate; /*passing the sample rate from buffer header*/
4461            /*check if this channel is a broadband one or SP
4462             * This simply check out the first two channel characters in order to conclude
4463             * whether it is a broadband (BH) or shorperiod (SP)*/
4464            switch(trace->chan[0])
4465            {
4466            case 'B':
4467                isBroadband = TRUE;
4468                break;
4469            case 'H':
4470                isBroadband = TRUE;
4471                break;
4472            default:
4473                isBroadband = FALSE;
4474                break;
4475            }
4476        }
4477        // Process samples
4478        if( strcmp( trace_header->datatype, "i2" ) == 0 ||
4479                strcmp(trace_header->datatype, "s2")==0 )
4480        {
4481            // Short samples
4482            for( i = 0; i < trace_header->nsamp; i++, npol++, trptr += 2 )
4483            {
4484                // Compute x coordinate
4485                pol[npol].x = ( int )( ( trace_header->starttime - starttime +
4486                                         ( double ) i * dt ) * xscale );
4487
4488                // Compute raw y coordinate without scaling or de-averaging
4489                pol[npol].y = ( int ) (*((short*)(trptr)));
4490
4491                // Add sample to average counter
4492                sampavg += ( double ) pol[npol].y;
4493                nsampavg++; // Increments number of samples to consider for average
4494
4495                // Consider sample for maximum (absolute) value
4496                if( pol[npol].y > sampmax || pol[npol].y < -sampmax ) sampmax = pol[npol].y;
4497            }
4498        }
4499        else if( strcmp( trace_header->datatype, "i4" ) == 0 ||
4500                 strcmp(trace_header->datatype, "s4")==0 )
4501        {
4502            // Integer samples
4503            for( i = 0; i < trace_header->nsamp; i++, npol++, trptr += 4 )
4504            {
4505                // Compute x coordinate
4506                pol[npol].x = ( int )( ( trace_header->starttime - starttime +
4507                                         ( double ) i * dt ) * xscale );
4508
4509                // Compute raw y coordinate without scaling or de-averaging
4510                pol[npol].y = *((int*)(trptr));
4511
4512                // Add sample to average counter
4513                sampavg += ( double ) pol[npol].y;
4514                nsampavg++; // Increments number of samples to consider for average
4515
4516                // Consider sample for maximum (non-absolute) value
4517                if( pol[npol].y > sampmax || pol[npol].y < -sampmax ) sampmax = pol[npol].y;
4518            }
4519        }
4520        else
4521        {
4522            // Unknown type of samples
4523            logit( "et", "trbf2gif: Unknown type of samples\n" );
4524            free( pol );
4525            return -1;
4526        }
4527        // At this point, the polygon is populated with samples from this trace
4528        // The sampmax, sampavg and nsampavg variables are also updated but have not
4529        // yet been applied to correct the polygon
4530    } // End of trace cycle
4531
4532    /*Filtering part*/
4533    if(SPfilter && isBroadband)
4534    {
4535        /*Starting the FILTER structures handle by the ioc_filter lib*/
4536        if((flag=initAllFilters(maxfilters)) != EW_SUCCESS)
4537        {
4538            logit("et","gmewhtmlemail: initAllFilters() cannot allocate Filters; exiting.\n");
4539        }
4540        if( (flag=initTransferFn()) != EW_SUCCESS)
4541        {
4542            logit("et","gmewhtmlemail: initTransferFn() Could not allocate filterTF.\n");
4543        }
4544        if(flag==EW_SUCCESS)
4545        {
4546            switch (flag=initChannelFilter(sr, sampavg/(double)nsampavg, isBroadband, &rfilter, maxfilters))
4547            {
4548            case  EW_SUCCESS:
4549
4550                if (Debug)
4551                    logit("et","gmewhtmlemail: Pre-filter ready for channel %s:%s:%s:%s\n",
4552                          trace->sta,trace->chan,trace->net,trace->loc);
4553                break;
4554
4555            case EW_WARNING:
4556
4557                printf("Unable to obtain a find an existing pre-filter or create a new (%s) pre-filter for sample rate %f; setting channel %s:%s:%s:%s bad\n",
4558                       (isBroadband ? "broadband" : "narrowband"),trace->samprate,
4559                       trace->sta,trace->chan,trace->net,trace->loc);
4560                break;
4561
4562            case EW_FAILURE:
4563
4564                logit("et",  "gmewhtmlemail Parameter passed in NULL; setting channel %s:%s:%s:%s bad\n",
4565                      trace->sta,trace->chan,trace->net,trace->loc);
4566                break;
4567
4568            default:
4569
4570                printf("Unknown error in obtaining a pre-filter for sample rate %f; setting channel %s:%s:%s:%s bad\n",
4571                       trace->samprate,trace->sta,trace->chan,trace->net,trace->loc);
4572                break;
4573            }
4574        }
4575        if( flag == EW_SUCCESS )
4576        {   /*The channel filters function went ok*/
4577            sampavg = 0;
4578            sampmax = 0;
4579            for(i=0; i<npol; i++) {
4580                y=(int)(filterTimeSeriesSample((double)pol[i].y,&rfilter));
4581                pol[i].y=y;
4582                sampavg+=(double)pol[i].y;
4583                if( pol[i].y > sampmax || pol[i].y < -sampmax ) sampmax = pol[i].y;
4584            }
4585            // Remove average from samples and normalize
4586            sampavg /=(double)nsampavg;
4587            sampmax -= sampavg;
4588            /*freeing the transfer function*/
4589            freeTransferFn();
4590        }
4591        else
4592        {
4593            /*Something was wrong and it is not possible to filter the samples*/
4594            // Remove average from samples and normalize
4595            sampavg /= ( double ) nsampavg; // Compute final sample average
4596            sampmax -= sampavg; // Correct sample maximum with average;
4597        }
4598    }/*end of filter part*/
4599    else
4600    {   /*it was not selected SPfilter*/
4601        sampavg /= ( double ) nsampavg; // Compute final sample average
4602        sampmax -= sampavg; // Correct sample maximum with average;
4603    }
4604    //printf("Buflen: %d   Avg: %f   Max: %f\n",buflen, sampavg,sampmax);
4605
4606    /* Correct Average */
4607    sampavg = 0;
4608    nsampavg = 0;
4609    for( i = 0; i < npol; i++ )
4610    {
4611        sampavg += ( double ) pol[i].y;
4612        nsampavg++;
4613    }
4614    sampavg /= nsampavg;
4615
4616    sampmax = 0;
4617    for( i = 0; i < npol; i++ ) {
4618        /* Correct Average */
4619        pol[i].y = ( int )( ( ( double ) pol[i].y - sampavg ) );
4620
4621        /* Compute new maximum after removing average */
4622        if( ( double ) pol[i].y > sampmax )
4623        {
4624            sampmax = ( double ) pol[i].y;
4625        }
4626        else if( ( double ) ( -pol[i].y ) > sampmax )
4627        {
4628            sampmax = -( double ) pol[i].y;
4629        }
4630    }
4631
4632    /* Scale image and shift to vertical middle */
4633    for( i = 0; i < npol; i++ )
4634    {
4635        pol[i].y =  ( int ) ( gif->sy / 2 ) -
4636                    ( int ) ( ( double ) pol[i].y / sampmax * ( double ) gif->sy / 2 );
4637    }
4638
4639    // Clear image
4640    gdImageFilledRectangle( gif, 0, 0, gif->sx, gif->sy, bcolor );
4641
4642    // Draw poly line
4643    gdImagePolyLine( gif, pol, npol, fcolor );
4644
4645
4646    // Free polygon memory
4647    free( pol );
4648    return 1;
4649}
4650
4651/****************************************************************************************
4652 * pick2gif: Plots a pick as a vertical line in the image. Includes the phase            *
4653 ****************************************************************************************/
4654int pick2gif(gdImagePtr gif, double picktime, char *pickphase,
4655             double starttime, double endtime, int fcolor)
4656{
4657    // Draw vertical line
4658    int pickpos = ( int ) ( ( double ) gif->sx / ( endtime - starttime ) *
4659                            ( picktime - starttime ) + 0.5 );
4660    gdImageLine(gif, pickpos, 0, pickpos, gif->sy, fcolor);
4661
4662    // Draw phase character
4663    gdImageChar(gif, gdFontSmall, pickpos - 8, 1, pickphase[0], fcolor);
4664
4665    return 1;
4666}
4667
4668/****************************************************************************************
4669 * Utility Functions                                                                    *
4670 ****************************************************************************************/
4671// Draw a poly line
4672void gdImagePolyLine( gdImagePtr im, gdPointPtr pol, int n, int c )
4673{
4674    int i;
4675    for( i = 0; i < ( n - 1 ); i++ )
4676        gdImageLine( im, pol[i].x, pol[i].y, pol[i + 1].x, pol[i + 1].y, c);
4677}
4678
4679/***************************************************************************************
4680 * gmtmap is a function to create a map with GMT, the format of the image
4681 * is png. gmtmap returns the string with the complementary html code where is added
4682 * the map. It is based in system calls.
4683 * Returns 0 if all is all right and -1 if a system call has failed
4684 * The map projection could be Mercator or Albers.
4685 * **************************************************************************************/
4686int gmtmap(char *request,SITE **sites, int nsites,
4687           double hypLat, double hypLon,int idevt)
4688{
4689    char command[MAX_GET_CHAR]; // Variable that contains the command lines to be executed through system() function.
4690    int i,j;
4691    /*apply some settings with gmtset for size font and others*/
4692    if(Debug) logit("ot","gmtmap(): gmtsets applying default fonts for event %d\n",idevt);
4693    if((j=system("gmtset ANNOT_FONT_SIZE_PRIMARY 8")) != 0)
4694    {
4695        logit("et","gmtmap(): Unable to set ANNOT_FONT_SIZE_PRIMARY for event %d\n",idevt);
4696        logit("et","gmtmap(): Please verify whether GMT is installed or the GMT bin PATH has been exported\n");
4697        return -1;
4698    }
4699    if((j=system("gmtset FRAME_WIDTH 0.2c")) != 0)
4700    {
4701        logit("et","gmtmap(): Unable to set FRAME_WIDTH for event %d\n",idevt);
4702        logit("et","gmtmap(): Please verify whether GMT is installed or the GMT bin PATH has been exported\n");
4703        return -1;
4704    }
4705    if((j=system("gmtset TICK_LENGTH 0.2c")) != 0)
4706    {
4707        logit("et","gmtmap(): Unable to set TICK_LENGTH for event %d\n",idevt);
4708        logit("et","gmtmap(): Please verify whether GMT is installed or the GMT bin PATH has been exported\n");
4709        return -1;
4710    }
4711    if((j=system("gmtset TICK_PEN 0.5p")) != 0)
4712    {
4713        logit("et","gmtmap(): Unable to set TICK_PEN for event %d\n",idevt);
4714        logit("et","gmtmap(): Please verify whether GMT is installed or the GMT bin PATH has been exported\n");
4715        return -1;
4716    }
4717    /*Mercator Projection*/
4718    if( Mercator )
4719    {
4720        if(Debug)logit("ot","gmtmap(): Mercator Projection used for event %d\n",idevt);
4721        /*creating the map with the hypocentral information. The epicenter will be the center of this
4722        * map and upper and  lower limits, for lat and lon, will be 4 degrees. Projection is Mercator*/
4723        if(Debug) logit("et","gmtmap(): creating the basemap for event %d\n",idevt);
4724        sprintf(command,"pscoast -R%f/%f/%f/%f -Jm%f/%f/0.5i -Df -G34/139/34 -B2 -P -N1/3/255 -N2/1/255/to"
4725                " -Slightblue -K > %s_%d.ps\n",hypLon-4.0,hypLon+4.0,hypLat-4.0,hypLat+4.0,hypLon,hypLat,HTMLFile,idevt);
4726        if( (j=system(command)) != 0)
4727        {
4728            logit("et","gmtmap(): Unable to create the basemap for event %d\n",idevt);
4729            logit("et","gmtmap(): Please verify whether GMT is installed or the GMT bin PATH has been exported\n");
4730            return -1;
4731        }
4732    }
4733    /*Albers Projection*/
4734    if( Albers )
4735    {
4736        if(Debug)logit("ot","gmtmap(): Albers Projection used for event %d\n",idevt);
4737        /*creating the map with the hypocentral information. The epicenter will be the center of this
4738        * map and upper and  lower limits, for lat and lon, will be 4 degrees. Albers Projection */
4739        if(Debug) logit("et","gmtmap(): creating the basemap for event %d\n",idevt);
4740        sprintf(command,"pscoast -R%f/%f/%f/%f -Jb%f/%f/%f/%f/0.5i -Df -G34/139/34 -B2 -P -N1/3/255 -N2/1/255/to"
4741                " -Slightblue -K > %s_%d.ps\n",hypLon-4.0,hypLon+4.0,hypLat-4.0,hypLat+4.0,hypLon,hypLat,hypLat-2,hypLat+2,HTMLFile,idevt);
4742        if( (j=system(command)) != 0)
4743        {
4744            logit("et","gmtmap(): Unable to create the basemap for event %d\n",idevt);
4745            logit("et","gmtmap(): Please verify whether GMT is installed or the GMT bin PATH has been exported\n");
4746            return -1;
4747        }
4748    }
4749    /*adding names (if StationNames = TRUE) and simbols for stations*/
4750    if(Debug) logit("et","gmtmap(): adding station location symbol\n");
4751    for(i=0; i<nsites; i++)
4752    {
4753        sprintf(command,"echo %f %f |psxy -R -J -St0.2 -W1/0 -Glightbrown -O -K >> %s_%d.ps",sites[i]->lon,sites[i]->lat,HTMLFile,idevt);
4754        if((j=system(command)) != 0)
4755        {
4756            logit("et","gmtmap(): Unable to add station symbols for station %s\n",sites[i]->name);
4757            return -1;
4758        }
4759        if(StationNames)
4760        {
4761            sprintf(command,"echo %f %f 4 0 1 CM %s |pstext -R -J -O -K -Glightbrown >>%s_%d.ps",sites[i]->lon,sites[i]->lat-0.15,sites[i]->name,HTMLFile,idevt);
4762            if((j=system(command)) != 0)
4763            {
4764                logit("et","gmtmap(): Unable to add station name for %s\n",sites[i]->name);
4765                return -1;
4766            }
4767        }
4768    }
4769    /*Adding the name of the cities listed in the file Cities*/
4770    if( strlen(Cities) >= 1 )
4771    {
4772        sprintf(command,"cat %s | awk '{print $1,$2-0.1}' | psxy -R -J -Sc0.05  -G255 -O -K >> %s_%d.ps",Cities,HTMLFile,idevt);
4773        if(Debug) logit("et","gmtmap(): adding a circle to symbolize a city \n");
4774        if( (j=system(command)) != 0 )
4775        {
4776            logit("et","gmtmap(): Unable to set the symbols for cities: event %d\n",idevt);
4777            return -1;
4778        }
4779        sprintf(command,"cat %s | pstext -R -J -O -G255  -S2  -K >> %s_%d.ps",Cities,HTMLFile,idevt);
4780        if(Debug) logit("et","gmtmap(): adding the names of cities\n");
4781        if((j=system(command)) != 0)
4782        {
4783            logit("et","gmtmap(): Unable to set the cities' names: event %d\n",idevt);
4784            return -1;
4785        }
4786    }
4787    /*Map legend for a map with Mercator Projection.
4788     *
4789     * The difference between this and the next if clause is the legend's size an position into the map*/
4790    if( strlen(MapLegend) > 1 && Mercator)
4791    {
4792        if(Debug) logit("et","gmtmap(): setting the size font to legend\n");
4793        sprintf(command,"gmtset ANNOT_FONT_SIZE_PRIMARY 6");
4794        system(command);
4795        if(Debug) logit("et","gmtmap(): Adding legend to map\n");
4796        sprintf(command,"pslegend -Dx0.48i/0.53i/0.9i/0.5i/TC -J -R -O -F %s -Glightyellow -K >> %s_%d.ps",MapLegend,HTMLFile,idevt);
4797        system(command);
4798    }
4799
4800    if( strlen(MapLegend) > 1 && Albers )
4801    {
4802        if(Debug) logit("et","gmtmap(): setting the size font to legend\n");
4803        sprintf(command,"gmtset ANNOT_FONT_SIZE_PRIMARY 6.5");
4804        system(command);
4805        if(Debug) logit("et","gmtmap(): Adding legend to map\n");
4806        sprintf(command,"pslegend -Dx0.60i/0.60i/0.9i/0.55i/TC -J -R -O -F %s -Glightyellow -K >> %s_%d.ps",MapLegend,HTMLFile,idevt);
4807        system(command);
4808    }
4809    /*drawing the epicentral symbol. A red start is the symbol*/
4810    if(Debug) logit("et","gmtmap(): Adding symbol to the epicenter\n");
4811    sprintf(command,"echo %f %f |psxy -R -J -Sa0.3 -W1/255/0/0 -G255/0/0 -O >> %s_%d.ps",hypLon,hypLat,HTMLFile,idevt);
4812
4813    if( (j=system(command)) != 0)
4814    {
4815        logit("et","gmtmap(): Unable to set the epicentral symbol for event %d\n",idevt);
4816        return -1;
4817    }
4818    if(Debug) logit("et","gmtmap(): Converting ps to png for event %d\n",idevt);
4819    sprintf(command,"ps2raster %s_%d.ps -A -P -Tg",HTMLFile,idevt);
4820    if( (j=system(command)) != 0)
4821    {
4822        logit("et","gmtmap(): Unable to convert from ps to png: event %d\n",idevt);
4823        return -1;
4824    }
4825    /*Resizing the png image to 500x500*/
4826    if(Debug) logit("et","gmtmap(): Changing the resolution of the image for event %d\n",idevt);
4827    sprintf(command,"convert -resize 500X500 %s_%d.png %s_%d.png",HTMLFile,idevt,HTMLFile,idevt);
4828    if( (j=system(command)) != 0)
4829    {
4830        logit("et","gmtmap(): Unable to resize the output file: event %d\n",idevt);
4831    }
4832
4833    /* Base of gmt map
4834    *******************************/
4835    snprintf(request, MAX_GET_CHAR, "<img class=\"MapClass\" alt=\"\" "
4836             "src=\"%s_%d.png\"/>",HTMLFile,idevt);
4837    return 0;
4838}
4839
4840/*******************************************************************************
4841 * Base64 encoder. Based on the suggestion presented on StackOverflow:         *
4842 * http://stackoverflow.com/questions/342409/how-do-i-base64-encode-decode-in-c*
4843 ******************************************************************************/
4844static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
4845                                'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
4846                                'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
4847                                'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
4848                                'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
4849                                'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
4850                                'w', 'x', 'y', 'z', '0', '1', '2', '3',
4851                                '4', '5', '6', '7', '8', '9', '+', '/'
4852                               };
4853static int  mod_table[] = {0, 2, 1};
4854
4855
4856unsigned char* base64_encode( size_t* output_length,
4857                              const unsigned char *data, size_t input_length )
4858{
4859    size_t i;
4860    int j;
4861    uint32_t    octet_a, octet_b, octet_c, triple;
4862    unsigned char* encoded_data;
4863
4864    *output_length = 4 * ((input_length + 2) / 3);
4865    //*output_length = ( ( input_length - 1 ) / 3 ) * 4 + 4;
4866
4867    encoded_data = ( unsigned char* ) malloc(
4868                       *output_length * sizeof( unsigned char ) );
4869    if( encoded_data == NULL ) return NULL;
4870
4871
4872    for( i = 0, j = 0; i < input_length; )
4873    {
4874        octet_a = (i < input_length) ? data[i++] : 0;
4875        octet_b = (i < input_length) ? data[i++] : 0;
4876        octet_c = (i < input_length) ? data[i++] : 0;
4877
4878        triple = ( octet_a << 0x10 ) + ( octet_b << 0x08 ) + octet_c;
4879
4880        encoded_data[j++] = encoding_table[( triple >> 3 * 6 ) & 0x3F];
4881        encoded_data[j++] = encoding_table[( triple >> 2 * 6 ) & 0x3F];
4882        encoded_data[j++] = encoding_table[( triple >> 1 * 6 ) & 0x3F];
4883        encoded_data[j++] = encoding_table[( triple >> 0 * 6 ) & 0x3F];
4884    }
4885    for ( j = 0; j < mod_table[input_length % 3]; j++ )
4886        encoded_data[ *output_length - 1 - j ] = '=';
4887
4888    return encoded_data;
4889}
4890
4891
4892/*******************************************************************************
4893 * Yield distance between (lat1,lon1) and(lat2,lon2) in specified units:       *
4894 * (M)iles, (K)ilometers, or (N)?
4895 ******************************************************************************/
4896#define pi 3.14159265358979323846
4897double distance(double lat1, double lon1, double lat2, double lon2, char unit) {
4898    double theta, dist;
4899    theta = lon1 - lon2;
4900    dist = sin(deg2rad(lat1)) * sin(deg2rad(lat2)) + cos(deg2rad(lat1)) * cos(deg2rad(lat2)) * cos(deg2rad(theta));
4901    dist = acos(dist);
4902    dist = rad2deg(dist);
4903    dist = dist * 60 * 1.1515;
4904    switch(unit) {
4905    case 'M':
4906        break;
4907    case 'K':
4908        dist = dist * 1.609344;
4909        break;
4910    case 'N':
4911        dist = dist * 0.8684;
4912        break;
4913    }
4914    return (dist);
4915}
4916
4917/********************************************************************
4918 *  This function converts decimal degrees to radians               *
4919 *******************************************************************/
4920double deg2rad(double deg) {
4921    return (deg * pi / 180);
4922}
4923
4924/********************************************************************
4925 *  This function converts radians to decimal degrees               *
4926 *******************************************************************/
4927double rad2deg(double rad) {
4928    return (rad * 180 / pi);
4929}
4930
4931
4932/*******************************************************************************
4933 * Read disclaimer text from specified file, store in DisclaimerText           *
4934 ******************************************************************************/
4935char* read_disclaimer( char* path ) {
4936    FILE *fp = fopen( path, "r" );
4937    long filesize, numread;
4938    char* temp_text;
4939    if ( fp == NULL ) {
4940        logit( "et", "Could not open disclaimer file: %s\n", path );
4941        return NULL;
4942    }
4943    fseek( fp, 0L, SEEK_END );
4944    filesize = ftell( fp );
4945    rewind( fp );
4946
4947    temp_text = malloc( filesize+5 );
4948    if ( temp_text == NULL ) {
4949        logit( "et", "Could no allocate space for disclaimer (%s)\n", path );
4950        fclose( fp );
4951        return NULL;
4952    }
4953
4954    numread = fread( temp_text, filesize, 1, fp );
4955    if ( numread != 1 ) {
4956        logit( "et", "Could not read disclaimer from file: %s\n", path );
4957        free( temp_text );
4958    }
4959    fclose( fp );
4960    return temp_text;
4961}
Note: See TracBrowser for help on using the repository browser.