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

Revision 8013, 194.4 KB checked in by alexander, 4 months ago (diff)

Fixing format string error with conversion factor

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