001package votorola.s.line; // Copyright 2009-2011, Michael Allan.  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE.
002
003import java.io.*;
004import java.net.*;
005import java.util.*;
006import java.util.regex.*;
007import javax.script.*;
008import votorola.a.*;
009import votorola.a.response.line.*;
010import votorola.g.*;
011import votorola.g.io.*;
012import votorola.g.lang.*;
013import votorola.g.logging.*;
014import votorola.g.net.*;
015import votorola.g.option.*;
016
017
018/** Main class of the executable <code>vomir</code> - a slapdash prototype of vote
019  * mirroring that worked against White House 2 servers.  It ran until early 2011, when
020  * those servers were shut down.  The <a
021  * href='http://reluk.ca/system/host/obsidian/home/v/votorola/in/vomir/' target='_top'>raw
022  * images</a> have since been frozen in place and are still being counted, as you can see
023  * in the results of poll <a
024  * href='http://reluk.ca:8080/v/w/Votespace?u=Mike-ZeleaCom&p=G!p!edor'
025  * target='_top'>G/p/edor</a>.  Red numeral 1's mark the votes that were mirrored.
026  *
027  *     @see <a href='../../../../../s/manual.xht#line-vomir' target='_top'>vomir</a>
028  */
029public @Warning("dead code") final class VOMir
030{
031
032
033    /** Executes from the command line.
034      *
035      *     @param argv command line argument array
036      */
037    public static @ThreadSafe void main( String[] argv )
038    {
039        LoggerX.i(VOMir.class).info( "vomir is running with arguments " + Arrays.toString( argv ));
040        final Map<String,Option> optionMap = CommandLine.compileBaseOptions();
041        final int aFirstNonOption = GetoptX.parse( "vomir", argv, optionMap );
042        if( aFirstNonOption < argv.length )
043        {
044            System.err.println( GetoptX.createUnexpectedArgWarning(
045              "vomir", argv[aFirstNonOption] ));
046            System.err.println( GetoptX.createHelpPrompt( "vomir" ));
047            System.exit( 1 );
048        }
049
050        if( optionMap.get("help").hasOccured() )
051        {
052            System.out.print
053            (
054              "Usage: vomir\n" +
055              "Pull remote votes for local mirroring.\n"
056            );
057            System.exit( 0 );
058        }
059
060        try
061        {
062            new VOMir().run();
063        }
064        catch( RuntimeException x ) { throw x; }
065        catch( Exception x )
066        {
067            System.err.print( "voter: fatal error" + Votorola.unmessagedDetails(x) + ": " );
068            x.printStackTrace( System.err );
069            System.exit( 1 );
070        }
071    }
072
073
074
075    private VOMir() throws IOException, javax.script.ScriptException, java.sql.SQLException,
076      URISyntaxException {}
077
078
079
080   // ------------------------------------------------------------------------------------
081
082
083    /** The pattern of a precedence section marker in White House 2.  The votes are
084      * sectioned by precedence.
085      */
086    private static @ThreadSafe final Pattern NAT_PRECEDENCE_PATTERN = Pattern.compile(
087      ">Priority #(\\d+)<" );
088
089
090
091    /** The pattern of a timestamp in White House 2.
092      */
093    private static @ThreadSafe final Pattern NAT_TIME_PATTERN = Pattern.compile(
094      "\"(\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d.+)\"" );
095       //  2  0  0  9 - 1  2 - 2  5 T 1  0 : 2  8 : 2  7 Z
096
097
098    /** The pattern of a user-page link in White House 2.
099      */
100    private static @ThreadSafe final Pattern NAT_USER_LINK_PATTERN = Pattern.compile(
101      "<a href=\"/users/(\\d+)-" );
102
103
104
105    /** Returns the next line that is neither empty, nor all whitespace.
106      */
107    private static @ThreadSafe String nextSolidLine( final BufferedReader in ) throws IOException
108    {
109        for( ;; )
110        {
111            final String s = in.readLine();
112            if( s == null ) return s;
113
114            if( s.length() == 0 ) continue;
115
116            for( int c = s.length() - 1; c >= 0; --c )
117            {
118                if( !Character.isWhitespace( s.charAt( c ))) return s;
119            }
120        }
121    }
122
123
124
125 // /** Constructs a new stream reader that is configured to read documents that are
126 //   * un-namespaced.
127 //   */
128 // private static @ThreadSafe XMLStreamReader newXMLStreamReader( final Reader reader )
129 //   throws XMLStreamException
130 // {
131 //     return VoterInputTable.newXMLStreamReader( reader );
132 // }
133
134
135
136    private final VoteServer.Run vsRun;
137    {
138        vsRun = new VoteServer( System.getProperty( "user.name" )).new Run(
139          /*isSingleThreaded*/true );
140        vsRun.singleServiceLock().lock(); // no need to unlock, single access
141    }
142
143
144
145    /** Attempts to pull from a White House 2 vote-server.  Creates a new source snap
146      * directory in the process, and sets it as the current one.  If the attempt fails,
147      * the previous snap directory is left current.
148      */
149    private void pullWH1_server( final String siteLoc, final String sourceMnemonic,
150      final HashMap<String,String> pollMap, final HashMap<String,MailOb> uidMap )
151    {
152        final File sourceDirectory = new File( vsRun.voteServer().inDirectory(),
153          "vomir" + File.separator + sourceMnemonic );
154        try
155        {
156            final File snapDirectory = OutputStore.mkdirsY4MDS( sourceDirectory, "snap" );
157            final String pollName = "G/p/edor";
158            final String priorityS = pollMap.get( pollName );
159             // To allow multiple priorities (proposals) per poll in future, prune
160             // duplicate votes based on the voter's ranking of the priority (vote
161             // precedence).
162            if( priorityS == null ) LoggerX.i(getClass()).config( "no '" + sourceMnemonic + "' priority page mapped to poll: " + pollName );
163
164            final File file = new File( snapDirectory, pollName + ".xml" );
165            FileX.ensuredirs( file.getParentFile() );
166            boolean isFileCleanlyTerminated = false;
167            final BufferedWriter o = new BufferedWriter( new OutputStreamWriter(
168              new FileOutputStream( file ), "UTF-8" ));
169            try
170            {
171                o.append( "<?xml version='1.0' encoding='UTF-8'?> <!--                                 -*-coding: utf-8;-*- -->\n"
172                        + "<in serviceName='" + pollName + "'>\n" );
173                pullWH2_poll( siteLoc, uidMap, priorityS, /*toPullEndorsers*/true,
174                  sourceDirectory, o );
175                pullWH2_poll( siteLoc, uidMap, priorityS, /*toPullEndorsers*/false,
176                  sourceDirectory, o );
177                o.append( "    </in>\n" );
178                isFileCleanlyTerminated = true;
179            }
180            finally
181            {
182                o.close();
183                if( !isFileCleanlyTerminated ) file.delete();
184            }
185
186            final File snapLink = new File( sourceDirectory, "_snap_current" );
187            FileX.symlink( snapDirectory.getName(), snapLink.getPath() );
188        }
189        catch( IOException x ) { LoggerX.i(getClass()).log( LoggerX.INFO, /*message*/"unable to complete pull from site " + siteLoc + ":" + Votorola.unmessagedDetails(x), x ); }
190    }
191
192
193
194    /** Pulls from a White House 2 poll (priority).
195      */
196    private void pullWH2_poll( final String siteLoc, HashMap<String,MailOb> uidMap,
197      final String priorityS, final boolean toPullEndorsers, final File sourceDirectory,
198      final BufferedWriter o ) throws IOException
199    {
200        final String priorityLoc = siteLoc + "/priorities/" + priorityS;
201        final String sourceMnemonic = sourceDirectory.getName();
202        final String candidateMailLoc = (toPullEndorsers? "x-":"o-") + sourceMnemonic
203          + "-" + priorityS;
204        final String docLoc = toPullEndorsers? priorityLoc: priorityLoc + "/opposers";
205        o.append( "    <pos>\n"
206                + "        <subj dom='zelea.com' loc='" + candidateMailLoc + "'/>\n"
207                + "        <doc url='" + docLoc + "'/>\n"
208                + "        </pos>\n" );
209
210        final HttpURLConnection http;
211        {
212            final URL url = new URL( priorityLoc + "/"
213              + (toPullEndorsers? "endorsers":"opposers") );
214            LoggerX.i(getClass()).fine( "requesting page " + url );
215            http = (HttpURLConnection)( url.openConnection() );
216        }
217        URLConnectionX.connect( http );
218        try
219        {
220            final BufferedReader in = new BufferedReader( new InputStreamReader(
221              http.getInputStream(), "UTF-8" ));
222            try
223            {
224             // final XMLStreamReader r = newXMLStreamReader( /*systemId*/priorityLoc, in );
225             //// not well formed, so parse it by pattern matching:
226                Matcher m = null;
227                String precedence = "1"; // till proven otherwise
228                for( ;; )
229                {
230                    String s = nextSolidLine( in );
231                    if( s == null ) break;
232
233                    m = NAT_PRECEDENCE_PATTERN.matcher( s );
234                    if( m.find() )
235                    {
236                        precedence = m.group( 1 );
237                        continue;
238                    }
239
240                    m = NAT_USER_LINK_PATTERN.matcher( s );
241                    if( m.find() )
242                    {
243                        s = nextSolidLine( in );
244                        if( s == null ) break;
245
246                        final String userNumberS = m.group( 1 );
247                        final MailOb voterMailOb = uidMap.get( userNumberS );
248                        if( voterMailOb == null ) continue; // unregistered
249
250                        m = NAT_TIME_PATTERN.matcher( s );
251                        if( m.find() )
252                        {
253                            final String timeS = m.group( 1 );
254                            o.append( "    <pos>\n"
255                                    + "        <subj dom='" + voterMailOb.dom + "' loc='" + voterMailOb.loc + "'/>\n"
256                                    + "        <doc url='" + docLoc + "'/>\n"
257                                    + "        <vote t='" + timeS + "'>\n"
258                                    + "            <obj dom='zelea.com' loc='" + candidateMailLoc + "'/>\n"
259                                    + "            <prec>" + precedence + "</prec>\n"
260                                    + "            </vote>\n"
261                                    + "        </pos>\n" );
262                        }
263                    }
264                }
265            }
266            finally{ in.close(); }
267        }
268        finally{ http.disconnect(); }
269    }
270
271
272
273    private void run()
274    {
275        pullWH1_server( "http://www.whitehouse2.org",  "natW", pollMap_natW, uidMap_natW );
276        pullWH1_server( "http://www.parliament2.ca",   "natP", pollMap_natP, uidMap_natP );
277        pullWH1_server( "http://au.nationbuilder.com", "natA", pollMap_natA, uidMap_natA );
278    }
279
280
281
282   // ` N a t - A ````````````````````````````````````````````````````````````````````````
283
284
285    /** Map of priority number-strings on the Australia2 server, keyed by poll name.
286      */
287    private final HashMap<String,String> pollMap_natA;
288    {
289        final HashMap<String,String> m = new HashMap<String,String>();
290        pollMap_natA = m;
291        m.put( "G/p/edor", "72" );
292    }
293
294
295
296    /** Map of email addresses keyed by user number-strings on the Australia2 server.
297      */
298    private final HashMap<String,MailOb> uidMap_natA;
299    {
300        final HashMap<String,MailOb> m = new HashMap<String,MailOb>();
301        uidMap_natA = m;
302        m.put( "136", new MailOb ( "mike", "zelea.com" ));
303    }
304
305
306
307   // ` N a t - P ````````````````````````````````````````````````````````````````````````
308
309
310    /** Map of priority number-strings on the Parliament2 server, keyed by poll name.
311      */
312    private final HashMap<String,String> pollMap_natP;
313    {
314        final HashMap<String,String> m = new HashMap<String,String>();
315        pollMap_natP = m;
316        m.put( "G/p/edor", "99" );
317    }
318
319
320
321    /** Map of email addresses keyed by user number-strings on the Parliament2 server.
322      */
323    private final HashMap<String,MailOb> uidMap_natP;
324    {
325        final HashMap<String,MailOb> m = new HashMap<String,MailOb>();
326        uidMap_natP = m;
327        m.put( "1", new MailOb ( "trevor", "canadaka.net" ));
328        m.put( "563", new MailOb ( "mike", "zelea.com" ));
329        m.put( "564", new MailOb ( "ThomasvonderElbe", "gmx.de" ));
330    }
331
332
333
334   // ` N a t - W ````````````````````````````````````````````````````````````````````````
335
336
337    /** Map of priority number-strings on the Whitehouse2 server, keyed by poll name.
338      */
339    private final HashMap<String,String> pollMap_natW;
340    {
341        final HashMap<String,String> m = new HashMap<String,String>();
342        pollMap_natW = m;
343        m.put( "G/p/edor", "2365" );
344    }
345
346
347
348    /** Map of email addresses keyed by user number-strings on the Whitehouse2 server.
349      */
350    private final HashMap<String,MailOb> uidMap_natW;
351    {
352        final HashMap<String,MailOb> m = new HashMap<String,MailOb>();
353        uidMap_natW = m;
354        m.put( "1", new MailOb ( "jim", "gilliam.com" )); // though gilliam.com is listed as for sale, he uses this address in lists etc.
355        m.put( "10333", new MailOb ( "mike", "zelea.com" ));
356        m.put( "10346", new MailOb ( "ThomasvonderElbe", "gmx.de" ));
357    }
358
359
360
361   // ====================================================================================
362
363
364    private static final class MailOb
365    {
366
367        MailOb( final String loc, final String dom )
368        {
369            this.loc = loc;
370            this.dom = dom;
371        }
372
373
374        private final String dom;
375
376
377        private final String loc;
378
379    }
380
381
382}