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

Revision 8019, 195.9 KB checked in by kevin, 3 months ago (diff)

Added Arias Intensity and ColorPGA option

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