ymdp 0.1.10 → 0.1.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. data/.gitignore +2 -0
  2. data/Gemfile +8 -1
  3. data/History.txt +13 -2
  4. data/Rakefile +7 -0
  5. data/VERSION +1 -1
  6. data/features/data/app/assets/yrb/en-US/keys_en-US.pres +1 -0
  7. data/features/data/app/javascripts/application.js +7 -0
  8. data/features/data/app/stylesheets/application.css +3 -0
  9. data/features/data/app/views/layouts/application.html.haml +25 -0
  10. data/features/data/app/views/page.html.haml +2 -0
  11. data/{spec → features}/data/config/config.yml +5 -6
  12. data/features/data/config/content.yml +2 -0
  13. data/features/data/config/jslint.js +1 -0
  14. data/features/data/config/servers.yml +10 -0
  15. data/features/step_definitions/ymdp_steps.rb +54 -0
  16. data/features/support/env.rb +40 -0
  17. data/features/ymdp.feature +61 -0
  18. data/lib/ymdp.rb +7 -12
  19. data/lib/ymdp/base.rb +16 -65
  20. data/lib/ymdp/commands/generate.rb +2 -4
  21. data/lib/ymdp/compiler/base.rb +10 -8
  22. data/lib/ymdp/compiler/domains.rb +2 -1
  23. data/lib/ymdp/compiler/template.rb +29 -18
  24. data/lib/ymdp/{processor → compressor}/compressor.rb +6 -1
  25. data/{spec/data/script → lib/ymdp/compressor}/yuicompressor-2.4.2.jar +0 -0
  26. data/lib/ymdp/configuration/config.rb +3 -4
  27. data/lib/ymdp/configuration/constants.rb +22 -19
  28. data/lib/ymdp/tasks/ymdp.rake +0 -2
  29. data/{spec/data/script → lib/ymdp/validator}/jslint.js +0 -0
  30. data/lib/ymdp/{processor → validator}/validator.rb +30 -38
  31. data/lib/ymdp/{application.rb → view/application.rb} +0 -0
  32. data/lib/ymdp/{application_view.rb → view/application_view.rb} +4 -4
  33. data/lib/ymdp/{asset_tag_helper.rb → view/asset_tag_helper.rb} +0 -0
  34. data/lib/ymdp/{tag_helper.rb → view/tag_helper.rb} +0 -0
  35. data/spec/application_spec.rb +2 -0
  36. data/spec/application_view_spec.rb +6 -6
  37. data/spec/compiler_template_spec.rb +4 -4
  38. data/spec/compressor_spec.rb +11 -7
  39. data/spec/configuration_spec.rb +3 -0
  40. data/spec/default_settings.rb +1 -1
  41. data/spec/domains_spec.rb +1 -1
  42. data/spec/spec_helper.rb +1 -0
  43. data/spec/stubs.rb +4 -1
  44. data/spec/validator_spec.rb +45 -40
  45. data/spec/ymdp_base_spec.rb +10 -7
  46. data/ymdp.gemspec +25 -76
  47. metadata +32 -76
  48. data/lib/ymdp/processor/form_post.rb +0 -62
  49. data/lib/ymdp/processor/w3c.rb +0 -24
  50. data/spec/data/Rakefile +0 -3
  51. data/spec/data/VERSION +0 -1
  52. data/spec/data/app/.gitignore +0 -1
  53. data/spec/data/app/assets/images/lightbox/lightbox_bg.png +0 -0
  54. data/spec/data/app/assets/javascripts/OpenMailIntl.js +0 -291
  55. data/spec/data/app/assets/javascripts/controls.js +0 -965
  56. data/spec/data/app/assets/javascripts/date.js +0 -104
  57. data/spec/data/app/assets/javascripts/dragdrop.js +0 -974
  58. data/spec/data/app/assets/javascripts/effects.js +0 -1123
  59. data/spec/data/app/assets/javascripts/lowpro.js +0 -320
  60. data/spec/data/app/assets/javascripts/prototype.js +0 -4874
  61. data/spec/data/app/assets/javascripts/scriptaculous.js +0 -68
  62. data/spec/data/app/assets/yrb/en-US/application_en-US.pres +0 -7
  63. data/spec/data/app/helpers/application_helper.rb +0 -3
  64. data/spec/data/app/javascripts/application.js +0 -580
  65. data/spec/data/app/javascripts/debug.js +0 -138
  66. data/spec/data/app/javascripts/flash.js +0 -96
  67. data/spec/data/app/javascripts/header.js +0 -13
  68. data/spec/data/app/javascripts/help.js +0 -76
  69. data/spec/data/app/javascripts/i18n.js +0 -235
  70. data/spec/data/app/javascripts/launcher.js +0 -159
  71. data/spec/data/app/javascripts/logger.js +0 -61
  72. data/spec/data/app/javascripts/tag_helper.js +0 -178
  73. data/spec/data/app/stylesheets/application.css +0 -0
  74. data/spec/data/app/stylesheets/ie.css +0 -0
  75. data/spec/data/app/stylesheets/ie6.css +0 -0
  76. data/spec/data/app/stylesheets/ie7.css +0 -0
  77. data/spec/data/app/stylesheets/ie8.css +0 -0
  78. data/spec/data/app/stylesheets/lightbox.css +0 -30
  79. data/spec/data/app/stylesheets/non_ie.css +0 -0
  80. data/spec/data/app/views/layouts/application.html.haml +0 -37
  81. data/spec/data/app/views/page.html.haml +0 -1
  82. data/spec/data/app/views/shared/_error.html.haml +0 -8
  83. data/spec/data/app/views/shared/_flash.html.haml +0 -2
  84. data/spec/data/app/views/shared/_javascripts.html.haml +0 -22
  85. data/spec/data/app/views/shared/_loading.html.haml +0 -13
  86. data/spec/data/app/views/shared/_stylesheets.html.haml +0 -23
  87. data/spec/data/config/categories.yml +0 -6
  88. data/spec/data/config/config.yml.example +0 -30
  89. data/spec/data/config/constants.rb +0 -52
  90. data/spec/data/config/content.yml +0 -2
  91. data/spec/data/config/servers.yml +0 -10
  92. data/spec/data/config/servers.yml.example +0 -10
  93. data/spec/data/lib/init.rb +0 -13
  94. data/spec/data/lib/tasks/environment.rake +0 -4
  95. data/spec/data/lib/tasks/keys.rake +0 -3
  96. data/spec/data/lib/tasks/setup.rake +0 -13
  97. data/spec/data/lib/tasks/ymdp.rake +0 -4
  98. data/spec/data/script/build +0 -9
  99. data/spec/data/script/config +0 -13
  100. data/spec/data/script/destroy +0 -18
  101. data/spec/data/script/generate +0 -4
  102. data/spec/data/script/gitrm +0 -17
  103. data/spec/data/script/growl +0 -6
  104. data/spec/data/script/images +0 -48
  105. data/spec/data/script/langs +0 -29
  106. data/spec/data/script/translate +0 -5
  107. data/spec/data/script/ymdt +0 -1895
  108. data/spec/data/script/ymdt.old +0 -1890
  109. data/test.rb +0 -46
@@ -1,1890 +0,0 @@
1
- #!/usr/bin/env php
2
- <?php
3
-
4
-
5
-
6
- //YMDT::fixup helper
7
- //Replaces all asset URLs in a file with the current versions.
8
- class AssetURL_Fixer
9
- {
10
- function __construct($mapAssetURLsByPath)
11
- {
12
- $this->mapAssetURLsByPath = $mapAssetURLsByPath;
13
- $this->changedFilePaths = array();
14
-
15
- //horrible hack to reverse engineer the 'appid_version' part of
16
- //asset URL for use when run() can't find full URL.
17
- $this->urlPrefix = null;
18
- foreach($this->mapAssetURLsByPath as $path=>$url){
19
- $urlParts = explode('/', $url);
20
- $this->urlPrefix = '/om/assets/' . $urlParts[3] . '/';
21
- break;
22
- }
23
- }
24
-
25
- function run($filepath)
26
- {
27
- $contents = file_get_contents($filepath);
28
- verify($contents !== FALSE,
29
- "Couldn't read " . $filepath);
30
- echo 'Fixing up ' . $filepath . ":\n";
31
- //Asset part of URL terminates in whitespace, ', ", ), ?, or &
32
- $results = preg_replace_callback('|/om/assets/.+?_\d+?/([^\'\\s">\)&\?]+)|',
33
- array($this, 'callback'),
34
- $contents);
35
- verify($results !== NULL, "Couldn't do fixup of " . $filepath);
36
- if($results != $contents){
37
- verify(file_put_contents($filepath, $results) !== FALSE);
38
- $this->changedFilePaths[] = $filepath;
39
- }
40
- else
41
- echo "\t(nothing to do)\n";
42
- }
43
-
44
- function callback($matches)
45
- {
46
- $fileURL = $matches[0];
47
- $relativePath = $matches[1];
48
- $key = "assets/$relativePath";
49
-
50
- /// Until we're willing to restrict partners from using programatically-constructed
51
- /// asset URLs (like pingg does for colored dot icons), can't make whole-URL validation
52
- /// failures an error, just a warning. For these, we'll stick to just fixing up the URL
53
- /// prefix and hope that it's referencing a legal asset.
54
-
55
- if(array_key_exists($key, $this->mapAssetURLsByPath)){
56
- $replacement = $this->mapAssetURLsByPath[$key];
57
- }
58
- else if($this->urlPrefix){
59
- echo "\t*Warning* can't find correct URL for " . $fileURL . "\n" .
60
- "\t\tMight not be a legal asset, will work off URL prefix\n";
61
- verify(count($this->mapAssetURLsByPath),
62
- "\t\tCan't even do that, app has no assets.");
63
- $replacement = preg_replace('|/om/assets/.+?_.+?/|', $this->urlPrefix, $fileURL);
64
- // echo "prefix is " . $this->urlPrefix . ",fileURL $fileURL , replacement $replacement\n";
65
- }
66
- else{
67
- echo "\t*Warning* can't find correct URL for " . $fileURL . "\n" .
68
- "\tThis app has no assets!";
69
- $replacement = $fileURL;
70
- }
71
-
72
- if($replacement !== $fileURL){
73
- echo "\treplacing " . $fileURL . "\n";
74
- echo "\t---> $replacement \n";
75
- return $replacement;
76
- }
77
- return $fileURL;
78
- }
79
- }
80
-
81
-
82
-
83
- //File-system utility functions. Mostly for dealing with ymdt local app working
84
- //directories.
85
-
86
- class FileSys
87
- {
88
- const APPID_FNAME = '.appid';
89
-
90
- //$paths is an array of (possibly relative) paths / patterns.
91
- //Expand directories to their file paths.
92
- //Allows globs.
93
- //All paths will be absolute.
94
- //Ignores root readme.txt
95
- static function expandPaths($paths)
96
- {
97
- $more_paths = array();
98
- foreach($paths as $ndx => $path){
99
- if(!is_dir($path))
100
- continue;
101
- unset($paths[$ndx]);
102
- $expanded = glob(realpath($path) . '/*', GLOB_MARK);
103
- if(!empty($expanded))
104
- $more_paths = array_merge($more_paths, $expanded);
105
- }
106
-
107
- if(empty($more_paths))
108
- return array_map('realpath', $paths);
109
-
110
- return self::expandPaths(array_merge($paths, $more_paths));
111
- }
112
-
113
- static function filterMetaFiles($paths, $basepath)
114
- {
115
- $newpaths = array();
116
- $metas = array($basepath.'/'.self::APPID_FNAME,
117
- $basepath.'/readme.txt');
118
-
119
- foreach($paths as $p){
120
- if(in_array($p, $metas))
121
- continue;
122
- if($p[0] == '.')
123
- continue;
124
- if($p[strlen($p)-1] == '~')
125
- continue; //emacs backup files
126
- $newpaths[] = $p;
127
- }
128
-
129
- return $newpaths;
130
- }
131
-
132
- static function filterNonTextPaths($paths)
133
- {
134
- $txt_extensions = array('htm', 'html', 'js', 'css', 'txt');
135
- $results = array();
136
- foreach($paths as $p){
137
- if(in_array(pathinfo($p, PATHINFO_EXTENSION), $txt_extensions))
138
- $results[] = $p;
139
- }
140
- return $results;
141
- }
142
-
143
- static function layout($appdir)
144
- {
145
- global $HELP_README;
146
-
147
- $subdirs = array($appdir.'/views', $appdir.'/assets');
148
- foreach($subdirs as $s){
149
- verify(file_exists($s) || self::mkdir($s));
150
- }
151
-
152
- $readme = $appdir.'/readme.txt';
153
- verify(file_put_contents($readme, $HELP_README),
154
- "Couldn't write $readme");
155
- }
156
-
157
- static function mkdir($path)
158
- {
159
- if(is_dir($path))
160
- return;
161
- return mkdir($path, 0755, true);
162
- }
163
-
164
- static function modTime($path)
165
- {
166
- $stat = stat($path);
167
- return $stat['mtime'];
168
- }
169
-
170
- static function nameFromConfFile($appdir)
171
- {
172
- $confFname = $appdir . '/config.xml';
173
- $xml = simplexml_load_file($confFname);
174
- verify(is_a($xml, 'SimpleXMLElement'),
175
- "Couldn't read XML conf from $confFname");
176
- return $xml->name;
177
- }
178
-
179
- //returns array(appid, basepath, subpath)
180
- //basepath is absolute path to app root, i.e. the dir that contains .appid
181
- //subpath is the path (file/dir/pattern) relative to basepath
182
- //filename is the filename (or terminating glob / null if dir)
183
- function parsePath($path)
184
- {
185
- $newpath = realpath($path);
186
- if(!$newpath){
187
- $newpath = realpath(dirname($path)) . '/' . basename($path);
188
- //really only support globs in filenames, no higher up the path
189
- verify($newpath, "unsupported glob in dirname $path ?");
190
- }
191
- $path = $newpath;
192
-
193
- //Keep on going up the path until we find the magic .appid file
194
- $base = $path;
195
- if(!is_dir($base))
196
- $base = dirname($base);
197
-
198
- while(true){
199
- $appid_path = $base . '/' . self::APPID_FNAME;
200
-
201
- if(file_exists($appid_path)){
202
- $appid = file_get_contents($appid_path);
203
- verify($appid != '', 'Empty .appid file?');
204
- $subpath = substr($path, strlen($base)+1);
205
-
206
- return array($appid, $base, $subpath);
207
- }
208
- if($base == '.' || $base == '/' || $base == '')
209
- break;
210
- $base = dirname($base);
211
- }
212
- verify(false, "Couldn't find .appid along $path");
213
- }
214
- };
215
-
216
-
217
-
218
- /* vim: set expandtab tabstop=4 shiftwidth=4: */
219
- // +----------------------------------------------------------------------+
220
- // | PHP Version 5 |
221
- // +----------------------------------------------------------------------+
222
- // | Copyright (c) 1997-2004 The PHP Group |
223
- // +----------------------------------------------------------------------+
224
- // | This source file is subject to version 3.0 of the PHP license, |
225
- // | that is bundled with this package in the file LICENSE, and is |
226
- // | available through the world-wide-web at the following url: |
227
- // | http://www.php.net/license/3_0.txt. |
228
- // | If you did not receive a copy of the PHP license and are unable to |
229
- // | obtain it through the world-wide-web, please send a note to |
230
- // | license@php.net so we can mail you a copy immediately. |
231
- // +----------------------------------------------------------------------+
232
- // | Author: Andrei Zmievski <andrei@php.net> |
233
- // +----------------------------------------------------------------------+
234
- //
235
- // $Id: Getopt.php,v 1.4 2007/06/12 14:58:56 cellog Exp $
236
-
237
-
238
- /**
239
- * Command-line options parsing class.
240
- *
241
- * @author Andrei Zmievski <andrei@php.net>
242
- *
243
- */
244
- class Console_Getopt {
245
- /**
246
- * Parses the command-line options.
247
- *
248
- * The first parameter to this function should be the list of command-line
249
- * arguments without the leading reference to the running program.
250
- *
251
- * The second parameter is a string of allowed short options. Each of the
252
- * option letters can be followed by a colon ':' to specify that the option
253
- * requires an argument, or a double colon '::' to specify that the option
254
- * takes an optional argument.
255
- *
256
- * The third argument is an optional array of allowed long options. The
257
- * leading '--' should not be included in the option name. Options that
258
- * require an argument should be followed by '=', and options that take an
259
- * option argument should be followed by '=='.
260
- *
261
- * The return value is an array of two elements: the list of parsed
262
- * options and the list of non-option command-line arguments. Each entry in
263
- * the list of parsed options is a pair of elements - the first one
264
- * specifies the option, and the second one specifies the option argument,
265
- * if there was one.
266
- *
267
- * Long and short options can be mixed.
268
- *
269
- * Most of the semantics of this function are based on GNU getopt_long().
270
- *
271
- * @param array $args an array of command-line arguments
272
- * @param string $short_options specifies the list of allowed short options
273
- * @param array $long_options specifies the list of allowed long options
274
- *
275
- * @return array two-element array containing the list of parsed options and
276
- * the non-option arguments
277
- *
278
- * @access public
279
- *
280
- */
281
- function getopt2($args, $short_options, $long_options = null)
282
- {
283
- return Console_Getopt::doGetopt(2, $args, $short_options, $long_options);
284
- }
285
-
286
- /**
287
- * This function expects $args to start with the script name (POSIX-style).
288
- * Preserved for backwards compatibility.
289
- * @see getopt2()
290
- */
291
- function getopt($args, $short_options, $long_options = null)
292
- {
293
- return Console_Getopt::doGetopt(1, $args, $short_options, $long_options);
294
- }
295
-
296
- /**
297
- * The actual implementation of the argument parsing code.
298
- */
299
- function doGetopt($version, $args, $short_options, $long_options = null)
300
- {
301
- // in case you pass directly readPHPArgv() as the first arg
302
- // if (PEAR::isError($args)) {
303
- // return $args;
304
- // }
305
- if (empty($args)) {
306
- return array(array(), array());
307
- }
308
- $opts = array();
309
- $non_opts = array();
310
-
311
- settype($args, 'array');
312
-
313
- if ($long_options) {
314
- sort($long_options);
315
- }
316
-
317
- /*
318
- * Preserve backwards compatibility with callers that relied on
319
- * erroneous POSIX fix.
320
- */
321
- if ($version < 2) {
322
- if (isset($args[0]{0}) && $args[0]{0} != '-') {
323
- array_shift($args);
324
- }
325
- }
326
-
327
- reset($args);
328
- while (list($i, $arg) = each($args)) {
329
-
330
- /* The special element '--' means explicit end of
331
- options. Treat the rest of the arguments as non-options
332
- and end the loop. */
333
- if ($arg == '--') {
334
- $non_opts = array_merge($non_opts, array_slice($args, $i + 1));
335
- break;
336
- }
337
-
338
- if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$long_options)) {
339
- $non_opts[] = $arg;
340
- // = array_merge($non_opts, array_slice($args, $i));
341
- // break;
342
- } elseif (strlen($arg) > 1 && $arg{1} == '-') {
343
- $error = Console_Getopt::_parseLongOption(substr($arg, 2), $long_options, $opts, $args);
344
- // if (PEAR::isError($error))
345
- // return $error;
346
- } elseif ($arg == '-') {
347
- // - is stdin
348
- $non_opts = array_merge($non_opts, array_slice($args, $i));
349
- break;
350
- } else {
351
- $error = Console_Getopt::_parseShortOption(substr($arg, 1), $short_options, $opts, $args);
352
- // if (PEAR::isError($error))
353
- // return $error;
354
- }
355
- }
356
-
357
- return array($opts, $non_opts);
358
- }
359
-
360
- /**
361
- * @access private
362
- *
363
- */
364
- function _parseShortOption($arg, $short_options, &$opts, &$args)
365
- {
366
- for ($i = 0; $i < strlen($arg); $i++) {
367
- $opt = $arg{$i};
368
- $opt_arg = null;
369
-
370
- /* Try to find the short option in the specifier string. */
371
- if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':')
372
- {
373
- return verify(false, "Console_Getopt: unrecognized option -- $opt");
374
- }
375
-
376
- if (strlen($spec) > 1 && $spec{1} == ':') {
377
- if (strlen($spec) > 2 && $spec{2} == ':') {
378
- if ($i + 1 < strlen($arg)) {
379
- /* Option takes an optional argument. Use the remainder of
380
- the arg string if there is anything left. */
381
- //$opts[] = array($opt, substr($arg, $i + 1));
382
- $opts[$opt] = substr($arg, $i + 1);
383
- break;
384
- }
385
- } else {
386
- /* Option requires an argument. Use the remainder of the arg
387
- string if there is anything left. */
388
- if ($i + 1 < strlen($arg)) {
389
- // $opts[] = array($opt, substr($arg, $i + 1));
390
- $opts[$opt] = substr($arg, $i + 1);
391
- break;
392
- } else if (list(, $opt_arg) = each($args)) {
393
- /* Else use the next argument. */;
394
- if (Console_Getopt::_isShortOpt($opt_arg) || Console_Getopt::_isLongOpt($opt_arg)) {
395
- verify(false, "Console_Getopt: option requires an argument -- $opt");
396
- }
397
- } else {
398
- return verify(false, "Console_Getopt: option requires an argument -- $opt");
399
- }
400
- }
401
- }
402
-
403
- // $opts[] = array($opt, $opt_arg);
404
- $opts[$opt] = $opt_arg;
405
- }
406
- }
407
-
408
- /**
409
- * @access private
410
- *
411
- */
412
- function _isShortOpt($arg)
413
- {
414
- return strlen($arg) == 2 && $arg[0] == '-' && preg_match('/[a-zA-Z]/', $arg[1]);
415
- }
416
-
417
- /**
418
- * @access private
419
- *
420
- */
421
- function _isLongOpt($arg)
422
- {
423
- return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' &&
424
- preg_match('/[a-zA-Z]+$/', substr($arg, 2));
425
- }
426
-
427
- /**
428
- * @access private
429
- *
430
- */
431
- function _parseLongOption($arg, $long_options, &$opts, &$args)
432
- {
433
- @list($opt, $opt_arg) = explode('=', $arg, 2);
434
- $opt_len = strlen($opt);
435
-
436
- for ($i = 0; $i < count($long_options); $i++) {
437
- $long_opt = $long_options[$i];
438
- $opt_start = substr($long_opt, 0, $opt_len);
439
- $long_opt_name = str_replace('=', '', $long_opt);
440
-
441
- /* Option doesn't match. Go on to the next one. */
442
- if ($long_opt_name != $opt) {
443
- continue;
444
- }
445
-
446
- $opt_rest = substr($long_opt, $opt_len);
447
-
448
- /* Check that the options uniquely matches one of the allowed
449
- options. */
450
- if ($i + 1 < count($long_options)) {
451
- $next_option_rest = substr($long_options[$i + 1], $opt_len);
452
- } else {
453
- $next_option_rest = '';
454
- }
455
- if ($opt_rest != '' && $opt{0} != '=' &&
456
- $i + 1 < count($long_options) &&
457
- $opt == substr($long_options[$i+1], 0, $opt_len) &&
458
- $next_option_rest != '' &&
459
- $next_option_rest{0} != '=') {
460
- return verify(false, "Console_Getopt: option --$opt is ambiguous");
461
- }
462
-
463
- if (substr($long_opt, -1) == '=') {
464
- if (substr($long_opt, -2) != '==') {
465
- /* Long option requires an argument.
466
- Take the next argument if one wasn't specified. */;
467
- if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args))) {
468
- return verify(false, "Console_Getopt: option --$opt requires an argument");
469
- }
470
- if (Console_Getopt::_isShortOpt($opt_arg) || Console_Getopt::_isLongOpt($opt_arg)) {
471
- return verify(false, "Console_Getopt: option requires an argument --$opt");
472
- }
473
- }
474
- } else if ($opt_arg) {
475
- return verify(false, "Console_Getopt: option --$opt doesn't allow an argument");
476
- }
477
-
478
- $opts[] = array('--' . $opt, $opt_arg);
479
- return;
480
- }
481
-
482
- return verify(false, "Console_Getopt: unrecognized option --$opt");
483
- }
484
-
485
- /**
486
- * Safely read the $argv PHP array across different PHP configurations.
487
- * Will take care on register_globals and register_argc_argv ini directives
488
- *
489
- * @access public
490
- * @return mixed the $argv PHP array or PEAR error if not registered
491
- */
492
- function readPHPArgv()
493
- {
494
- global $argv;
495
- if (!is_array($argv)) {
496
- if (!@is_array($_SERVER['argv'])) {
497
- if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
498
- return verify(false,
499
- "Console_Getopt: Could not read cmd args (register_argc_argv=Off?)");
500
- }
501
- return $GLOBALS['HTTP_SERVER_VARS']['argv'];
502
- }
503
- return $_SERVER['argv'];
504
- }
505
- return $argv;
506
- }
507
-
508
- }
509
-
510
-
511
-
512
-
513
-
514
- $HELP_README = <<<EOT
515
- This dir contains files to make a Yahoo! Mail Application.
516
-
517
- Run ymdt to get an overview on how to exchange these files with a Yahoo!
518
- Mail development server.
519
-
520
- What's here to start with, for a minimal app?
521
-
522
- config.xml: app's config XML
523
-
524
- auth.xml: app's authentication XML
525
-
526
- readme.txt: this file, ignored by Yahoo! tools
527
-
528
- .appid: metadata for use by Yahoo! tools. Do not modify.
529
-
530
- views/: subdirectory where you may add view html files
531
-
532
- assets/: subdirectory where you may add non-view assets
533
- it may contain other subdirectories
534
-
535
- Portable Network Graphics files you could add at this directory's top-level:
536
-
537
- icon.png: a 16x16 pixel image for use as an icon
538
-
539
- thumbnail.png: a 64x64 pixel image for use in an app gallery
540
-
541
- full.png: a 300x250 pixel image for preview / help display purposes
542
-
543
- EOT;
544
-
545
- $HELP_USAGE = <<<EOT
546
- Usage:
547
-
548
- ymdt <command> [flags] [command arguments]
549
-
550
- Type 'ymdt help <command>' for help on a specific command.
551
-
552
- Available commands:
553
- apps lists all of the user's apps
554
- create creates a new app
555
- destroy delete the entire app
556
- del deletes one of an app's files
557
- dev develop-o-matic mode
558
- fixup rewrite app's views and text assets so that asset URLs are correct
559
- get get latest of an app's file(s) from dev server
560
- help get help on a specific command
561
- ls list all of an app's file(s) and their URLs
562
- publish publish an app
563
- put upload apps file(s) to dev server
564
- tester list, invite, or delete testers
565
- EOT;
566
-
567
- $HELP_COMMON_OPTIONS = <<<EOT
568
- Global Options:
569
-
570
- -h<host>
571
- Override default Yahoo! Mail developer hostname.
572
- (Default is developer.mail.yahoo.com)
573
-
574
- -u<user>
575
- Specify user ID. If this option is omitted, the user ID is either
576
- figured from cookies or by prompting the user.
577
-
578
- After authentication, a token is normally cached in a cookie file in your
579
- home directory. It does not contain your password. This time-sensitive
580
- token will be reused on subsequent invocations to avoid logging on. Use
581
- the -d flag to avoid writing that file and force login on every invocation.
582
-
583
- -d
584
- Don't save auth token to file.
585
-
586
- -p<password>
587
- You're better off not using this option and letting the program offer you a
588
- masked prompt for password entry.
589
-
590
- -k
591
- Disables SSL host verification, use if the host you're connecting to is
592
- using self-signed SSL certs.
593
-
594
- EOT;
595
-
596
- $HELP_APPS = <<<EOT
597
- usage: apps
598
-
599
- Lists all of the logged-on user's apps.
600
-
601
- Each app is reported as a line beginning with the appid, followed by a tab and
602
- then the app name.
603
-
604
- EOT;
605
-
606
- $HELP_CREATE = <<<EOT
607
- usage: create <path>
608
-
609
- Create an app on the development server, with a local working copy kept
610
- in <path>.
611
-
612
- If <path> doesn't already contain an app, a minimal app will be created.
613
-
614
- If <path> already contains an app, a new app will be created based on
615
- the app files in <path>, and <path> will be converted to a valid working
616
- directory for the newly-created app.
617
-
618
- In either case, the app name will be set to '<path>', but you can modify it
619
- to be different from '<path>' by editing the app's config.xml.
620
-
621
- For a description of the files comprising an app, see <path>/readme.txt.
622
-
623
- Options:
624
- -A<pub_appid>:<priv_appid>
625
- -A<pub_appid>:
626
- -A:<priv_appid>
627
- Usually, the server allocates a globally-unique appid for both the public
628
- and private (development) versions of the app. This option allows admins
629
- to explicitly specify a public appid, private appid, or both. Fails if
630
- any of the specified appids already exist or if the logged-on user doesn't
631
- have admin rights. Note the magic ':', it must be there even if you're
632
- not specifying both appids.
633
-
634
- Examples:
635
- create a new app in directory ~/apps/llama:
636
- ymdt create ~/apps/llama
637
-
638
- create a new app in directory ~/apps/lindy with pub appid 123 and
639
- private appid 456:
640
- ymdt -A123:456 create ~/apps/lindy
641
-
642
- create a new app in directory ~/apps/lindy with autogenerated
643
- public appid and private appid 456:
644
- ymdt -A:456 create ~/apps/lindy
645
-
646
- create a new app in directory ~/apps/lindy with public appid 123 and
647
- an autogenerated private appid:
648
- ymdt -A123: create ~/apps/lindy
649
-
650
- EOT;
651
-
652
- $HELP_DEL = <<<EOT
653
- usage: del <path>
654
-
655
- Delete one of an app's files.
656
-
657
- <path> must be to a single file in an app's local working directory.
658
-
659
- Examples:
660
- delete asset 'lark.jpg' for app in directory ./birds:
661
- ymdt del ./birds/assets/lark.jpg
662
-
663
- EOT;
664
-
665
- $HELP_DEV = <<<EOT
666
- usage: dev <path>
667
-
668
- Enter dev-o-matic mode. <path> must be to the root of an app's local working
669
- directory. Will do a get of app, then continuously watch for local changes and
670
- update the server.
671
-
672
- EOT;
673
-
674
- $HELP_DESTROY = <<<EOT
675
- usage: destroy [<path>]
676
-
677
- Destroy an application. Must specify the path or the -a flag.
678
-
679
- -a<appid>
680
- <appid> is the application's private appid.
681
-
682
- -z
683
- Admin option. Really, really delete it from the development server.
684
-
685
- EOT;
686
-
687
- $HELP_FIXUP = <<<EOT
688
- usage: fixup <path>
689
-
690
- When your app is published, the in-development version of your app is copied to
691
- the public version of your app, which is visible to the world. The first time
692
- after publication that you upload (put) assets to the development server, the
693
- asset URLs change. Use this command to fixup up all of the asset URLs in your
694
- view HTML and your text assets.
695
-
696
- Recommended best practice: after your app is published (by an admin), run fixup,
697
- but backup your personal source first. (Your app is in source control, right?)
698
-
699
- Options:
700
- -z
701
- Suppress autoput. Normally, fixup executes these steps:
702
- 1) Uploads all of your app's files to the development server.
703
- 2) Fetches the latest asset URLs from the server.
704
- 3) Rewrites views and text type assets to use the latest asset URLs.
705
- 4) Uploads any files modified in step 3.
706
- Using the -z option suppresses steps 1 and 4. This is handy if you want
707
- to avoid step 1 when you know the assets have already been uploaded once
708
- since the last publish or if you want to manually review the results of 3
709
- before uploading files to the development server.
710
-
711
- -n
712
- Use the application name from the app's conf.xml to lookup the app's
713
- private appid on the server and modify the .appid file in your local
714
- working copy. See: put -n.
715
-
716
- -s
717
- Sync. Same as for put.
718
- EOT;
719
-
720
- $HELP_GET = <<<EOT
721
- usage: get <path>
722
-
723
- Get latest of an app's file(s) from dev server. Normally, <path> is to the root
724
- or some subpath of the app's local working copy. Wildcards (?*) allowed below
725
- app root, but only if <path> is quoted.
726
-
727
- Options:
728
- -a<appid>
729
- Get an app that exists on the server, but for which you don't yet have
730
- a working copy. In this case, <path> must be to an empty directory.
731
-
732
- -s
733
- Sync: Do the get, but also delete any local files below <path> that
734
- aren't present on the server.
735
-
736
- -y
737
- Autoyes: don't prompt user to confirm local deletes, use with care!
738
-
739
- Examples:
740
- get latest of all files for app 045fa65e3, using directory duck as the
741
- local working directory:
742
- ymdt -a045fa65e3 get duck
743
-
744
- get latest of all files for app in directory ./scrooge:
745
- ymdt get ./scrooge
746
-
747
- get latest of all assets for app in directory ./mcduck:
748
- ymdt get ./mcduck/assets
749
-
750
- get latest of config for app in directory ./ham:
751
- ymdt get ./ham/config.xml
752
-
753
- get latest of .js files in ./ham/assets
754
- ymdt get './ham/assets/*.js'
755
-
756
- EOT;
757
-
758
- $HELP_HELP = <<<EOT
759
- usage: help <command>
760
-
761
- Give help for a particular command.
762
-
763
- For general usage, run ymdt without any arguments.
764
-
765
- EOT;
766
-
767
- $HELP_LS = <<<EOT
768
- usage: ls <path>
769
-
770
- List server-side files and their URLs. Handy for figuring out how to reference
771
- your assets from views or other assets.
772
-
773
- <path> must minimally be an app's working directory, in which case all of the
774
- files comprising the app will be listed. <path> may also specify a particular
775
- subdir or file inside the app's working directory. Wildcards (*?) allowed below
776
- app's working directory root.
777
-
778
- Examples:
779
- list all assets for app in directory ~/apps/pigeon:
780
- ymdt ls ~/apps/pigeon/assets
781
-
782
- list all .jpg assets for app in directory ~/apps/zebra:
783
- ymdt ls '~/apps/zebra/assets/*.jpg'
784
- # note quotes to avoid shell expansion
785
-
786
- EOT;
787
-
788
- $HELP_PUBLISH = <<<EOT
789
- usage: publish [<path>]
790
-
791
- Update the publically-visible version of your app on the development server to
792
- be the same as the latest uploaded private (in-development) version of an app.
793
- <path> is the root of the private version's local working copy. It is only used
794
- to figure out the right appid. The public app update is peformed only using the
795
- private app files already uploaded to the server.
796
-
797
- Requires admin rights.
798
-
799
- You must use the -a option if you omit <path>.
800
-
801
- Options:
802
- -a<appid>
803
- Specify the private appid of the app you'd like to publish.
804
-
805
- -z
806
- Suppress asset URL validation.
807
-
808
- EOT;
809
-
810
- $HELP_PUT = <<<EOT
811
- usage: put <path>
812
-
813
- Upload files from local app working copy to the server. <path> is the root or
814
- some subpath of the app's local working copy. Wildcards (?*) allowed below app
815
- root, but only if <path> is quoted.
816
-
817
- Options:
818
- -a<appid>
819
- Explicitly specify the appid of the destination app on the server.
820
- Normally the .appid in the root directory of the app's local working
821
- copy determines what app is modified on the server. Since you may
822
- only modify the private (in-development) version of your app, <appid>
823
- must be a private appid.
824
-
825
- -n
826
- Use the application name from the app's conf.xml to lookup the app's
827
- private appid on the server and modify the .appid file in your local
828
- working copy. For situations where you created the local working copy
829
- by doing a get -a with a public appid or if you've done a get -h from
830
- a different server. Revises the .appid so that future puts will apply
831
- to the private app on the correct server.
832
-
833
- -s
834
- Sync: Do the put, but also delete any files on the server that aren't
835
- present in the local working copy.
836
-
837
- -y
838
- Autoyes: don't prompt user to confirm server deletes, use with care!
839
-
840
- Examples:
841
- upload all files for app in directory ./mcduck:
842
- ymdt put ./mcduck
843
-
844
- upload all assets for app in directory ./phish:
845
- ymdt put ./phish/assets
846
-
847
- upload latest of .js files in ./ham/assets
848
- ymdt put './ham/assets/*.js'
849
-
850
- EOT;
851
-
852
- $HELP_TESTER = <<<EOT
853
- usage: tester <ls|invite|del> [tester email address or yid]
854
-
855
- tester ls will display your tester list.
856
-
857
- invite <tester email address> will invite someone to be a tester.
858
-
859
- del <tester Yahoo! ID> will delete an existing tester.
860
-
861
- EOT;
862
-
863
- $HELP_UPGRADE = <<<EOT
864
- usage: upgrade
865
-
866
- Download the latest version of ymdt and replace the running script with it.
867
-
868
- EOT;
869
-
870
-
871
-
872
-
873
- //Functions for logging onto Bouncer and Yahoo!
874
- class Login
875
- {
876
- static function isWindows()
877
- {
878
- //better way to do this?
879
- if(getenv('HOME'))
880
- return false;
881
- return true;
882
- }
883
-
884
- static function getHomeDir()
885
- {
886
- $home = getenv('HOME');
887
- return $home ? $home : getenv('HOMEPATH'); //windows
888
- }
889
-
890
- static function promptUserInput($prompt = 'username:', $echo = true)
891
- {
892
- printf($prompt);
893
- $stty = '';
894
- if(!$echo){
895
- $stty = `stty -g`;
896
- system("stty -echo");
897
- }
898
- $input = trim(fgets(STDIN));
899
- if(!$echo)
900
- system("stty $stty");
901
-
902
- return $input;
903
- }
904
-
905
- static function promptCredentials($userPrompt, $passPrompt)
906
- {
907
- return array(self::promptUserInput($userPrompt),
908
- self::promptUserInput($passPrompt, false));
909
- }
910
-
911
- //on failure: returns false
912
- //on success: returns cookie string appropriate for curl_setopt(,CURLOPT_COOKIE,)
913
- static function authBouncer($bid, $password, $cookie_file_name, $use_guesthouse)
914
- {
915
- $url = "https://bouncer.by.corp.yahoo.com/login/";
916
- if($use_guesthouse)
917
- $url = "https://bouncer.gh.corp.yahoo.com/login/";
918
-
919
- $ch = curl_init();
920
- $timeout = 0;
921
-
922
- curl_setopt($ch, CURLOPT_POST, TRUE);
923
- curl_setopt($ch, CURLOPT_POSTFIELDS,
924
- array('action' => 'login',
925
- 'id' => $bid,
926
- 'pass_word' => $password));
927
-
928
- curl_setopt($ch, CURLOPT_URL, $url);
929
- curl_setopt($ch, CURLOPT_HEADER, TRUE);
930
- if($cookie_file_name){
931
- curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file_name);
932
- curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file_name);
933
- }
934
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
935
- curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
936
- if(self::isWindows()){
937
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
938
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
939
- }
940
-
941
- $result = curl_exec($ch);
942
- curl_close($ch);
943
- if($cookie_file_name)
944
- chmod($cookie_file_name, 0600);
945
- if($result === false || preg_match("/YCorp=/", $result) != 1){
946
- return false;
947
- }
948
-
949
- preg_match_all('|Set-Cookie: (.*);|U', $result, $matches);
950
- return implode(';', $matches[1]);
951
- // return true;
952
- }
953
-
954
- static function authYahoo($yid, $passwd, $cookie_file_name)
955
- {
956
- $agent = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US;'.
957
- 'rv:1.9.0.5) Gecko/2008120121 Firefox/3.0.5';
958
- $ch = curl_init();
959
- curl_setopt($ch, CURLOPT_USERAGENT, $agent);
960
- curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file_name);
961
- curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file_name);
962
- $postFields = "&login=$yid&passwd=$passwd";
963
- curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
964
- curl_setopt($ch, CURLOPT_URL, 'http://login.yahoo.com');
965
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
966
- $result = curl_exec($ch);
967
- curl_close($ch);
968
-
969
- return self::extractCookieString($cookie_file_name) != null;
970
- }
971
-
972
- static function extractCookieString($cookie_file_name)
973
- {
974
- if (!file_exists($cookie_file_name))
975
- return null;
976
- $file = fopen($cookie_file_name, 'r');
977
- if (!$file)
978
- return null;
979
-
980
- $regex = '/^\.yahoo\.com\tTRUE\t\/\tFALSE\t\d+\t(\w+)\t(.*)$/';
981
- while ($line = fgets($file)) {
982
- if (!preg_match($regex, $line, $matches))
983
- continue;
984
-
985
- $key = $matches[1];
986
- $val = $matches[2];
987
- $cookies[] = "$key=$val";
988
- }
989
- fclose($file);
990
- if(!count($cookies))
991
- return null;
992
- $cstr = join('; ', $cookies);
993
- return $cstr;
994
- }
995
-
996
- static function extractBID_FromCookie($cookie_file_name)
997
- {
998
- if(!file_exists($cookie_file_name))
999
- return null;
1000
-
1001
- static $regex = '/YBY\sid%3D[0-9]+%26userid%3D([^%]+)%26/';
1002
- $matches = array();
1003
- $contents = file_get_contents($cookie_file_name);
1004
- $count = preg_match($regex, $contents, $matches);
1005
- if($count != 1){
1006
- return null;
1007
- }
1008
- return $matches[1];
1009
- }
1010
-
1011
- }
1012
-
1013
-
1014
-
1015
- //Wraps calls to the development server.
1016
- class WebServiceClient
1017
- {
1018
- function __construct($hostname, $cookieFname, $lamessl, $uname, $passwd)
1019
- {
1020
- $this->hostname = $hostname ? $hostname : 'developer.mail.yahoo.com';
1021
- echo "Developer server: " . $this->hostname . "\n";
1022
- $this->cookieFname = $cookieFname;
1023
- $this->lamessl = $lamessl;
1024
- $this->uname = $uname;
1025
- $this->passwd = $passwd;
1026
- $this->wssid = null;
1027
- }
1028
-
1029
- function call($op, &$msg, $fields = array(), $format = 'json',
1030
- $checkStatus = true, $needLogin = true)
1031
- {
1032
- if(!$this->wssid && $needLogin)
1033
- $this->login();
1034
-
1035
- $url = 'https://' . $this->hostname;
1036
- if(strpos($op, 'admin.') !== false)
1037
- $url = 'http://' . $this->hostname . ':9999';
1038
-
1039
- $url .= '/om/api/1.0/openmail.' . $op;
1040
-
1041
- if($this->wssid){
1042
- $url .= '?bycrumb='.$this->wssid;
1043
- }
1044
-
1045
- $ch = curl_init();
1046
- $timeout = 0;
1047
- curl_setopt($ch, CURLOPT_POST, TRUE);
1048
- curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
1049
- curl_setopt($ch, CURLOPT_URL, $url);
1050
- curl_setopt($ch, CURLOPT_HEADER, TRUE);
1051
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1052
- curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
1053
- if($this->cookieFname)
1054
- curl_setopt($ch, CURLOPT_COOKIEFILE, $this->cookieFname);
1055
- else
1056
- curl_setopt($ch, CURLOPT_COOKIE, $this->cookies);
1057
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
1058
- if($this->lamessl)
1059
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
1060
-
1061
- $result = curl_exec($ch);
1062
- if(curl_errno($ch)){
1063
- $msg = 'Curl failure: ' . curl_error($ch);
1064
- }
1065
-
1066
- $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1067
- $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1068
-
1069
- curl_close($ch);
1070
- if(file_exists($this->cookieFname))
1071
- chmod($this->cookieFname, 0600);
1072
-
1073
- $content = substr($result, $header_size);
1074
-
1075
- $rval = null;
1076
- if($format){
1077
- if($format == 'json')
1078
- $rval = json_decode($content);
1079
- elseif($format == 'xml')
1080
- $rval = simplexml_load_string(trim($content));
1081
- if(!get_class($rval) && !is_array($rval))
1082
- $rval = null;
1083
- }
1084
-
1085
- if($http_code != '200'){
1086
- $msg = "$op invoke failed (HTTP $http_code).";
1087
- if($rval && property_exists($rval, 'message'))
1088
- $msg .= "\nServer says: {$rval->message}";
1089
- verify(false, $msg);
1090
- }
1091
-
1092
- if(!$format)
1093
- return $content;
1094
-
1095
- verify($rval !== null,
1096
- "Unexpected webservice result from $op: \n$format:\n\t[$content]");
1097
-
1098
- //Would be nice if ws results were a little more standardized, a la JSON-RPC.
1099
- //But, they're not. . .
1100
- if($checkStatus && $rval->status != 200){
1101
- $msg = property_exists($rval, 'message') ? $rval->message : '';
1102
- $msg = $msg . " (status: {$rval->status})";
1103
- verify(false, $msg);
1104
- }
1105
-
1106
- return $rval;
1107
- }
1108
-
1109
- function getWSSID()
1110
- {
1111
- //This call, unlike all the others, returns in XML (since JSON is
1112
- //security no-no for wssid.)
1113
- $result = $this->call('dev.file.init', $msg, array(), 'xml', true,
1114
- false);
1115
-
1116
- verify($result && property_exists($result, 'wssid'),
1117
- 'unable to retrieve wssid');
1118
-
1119
- $this->wssid = $result->wssid;
1120
- if(property_exists($result, 'version') &&
1121
- YMDT_Version < $result->version){
1122
- $latest = $result->version;
1123
- echo "***You are running an outdated version of ymdt ".
1124
- "(latest is $latest, you have " . YMDT_Version . ".).\n".
1125
- "***Please run: ymdt upgrade\n\n";
1126
- }
1127
- }
1128
-
1129
- function login()
1130
- {
1131
- $uname = $this->uname;
1132
- $passwd = $this->passwd;
1133
-
1134
- $use_guesthouse = strpos($this->hostname, 'corp.yahoo.com') === false;
1135
-
1136
- //Username changed? Blow away cookie file.
1137
- $prevUsername = Login::extractBID_FromCookie($this->cookieFname);
1138
- if($uname && $prevUsername != $uname){
1139
- verify(!file_exists($this->cookieFname) ||
1140
- unlink($this->cookieFname),
1141
- "login fought {$this->cookieFname} and lost");
1142
- }
1143
-
1144
- //If we can't get a crumb, try logging into backyard to refresh cookie,
1145
- //then try getting crumb again.
1146
- try {
1147
- $this->getWSSID();
1148
- if($this->wssid)
1149
- return;
1150
- } catch (Exception $e) {}
1151
-
1152
- if(!$uname){
1153
- $prompt = 'Backyard ID: ';
1154
- if($use_guesthouse)
1155
- $prompt = 'Guesthouse ID: ';
1156
- list($uname, $passwd) = Login::promptCredentials($prompt,
1157
- 'Password: ');
1158
- }
1159
- elseif(!$passwd){
1160
- $passwd = Login::promptUserInput('Password:', false);
1161
- }
1162
-
1163
- $this->cookies = Login::authBouncer($uname, $passwd, $this->cookieFname, $use_guesthouse);
1164
- verify($this->cookies, 'login failed');
1165
- echo "\nlogin successful\n";
1166
- if($this->cookieFname){
1167
- echo "\nAn authentication token has been stored in " .
1168
- $this->cookieFname . "\nUntil its expiration,".
1169
- "it will be used by future ymdt invocations to avoid having to" .
1170
- " relogin.\nTo disable this behavior, use the -d option.\n\n";
1171
- }
1172
- $this->getWSSID();
1173
- }
1174
-
1175
- function appList()
1176
- {
1177
- $msg = null;
1178
- $fields = array('ignorePublicationStatus' => 'true');
1179
- verify(($result = $this->call('dev.app.list', $msg, $fields, 'json',
1180
- false)) !== null,
1181
- "No webservice result.");
1182
- return $result;
1183
- }
1184
-
1185
- function ls($appid, $subpath = null)
1186
- {
1187
- $fields = array('app' => $appid);
1188
- if($subpath){
1189
- $fields['path'] = $subpath;
1190
- }
1191
-
1192
- $result = $this->call('dev.file.ls', $msg, $fields);
1193
- return get_object_vars($result->data);
1194
- }
1195
-
1196
- //upgrade helper, not really a ws call
1197
- function fetchLatestScript()
1198
- {
1199
- $ch = curl_init();
1200
- $timeout = 0;
1201
- $url = 'https://' . $this->hostname . '/openmail/assets/ymdt';
1202
- curl_setopt($ch, CURLOPT_URL, $url);
1203
- curl_setopt($ch, CURLOPT_HEADER, TRUE);
1204
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1205
- curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
1206
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
1207
- if($this->lamessl)
1208
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
1209
- $result = curl_exec($ch);
1210
- $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1211
- $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1212
- curl_close($ch);
1213
- $content = substr($result, $header_size);
1214
- verify($http_code == '200' && !empty($content),
1215
- "Upgrade fetch failed (HTTP $http_code).");
1216
- return $content;
1217
- }
1218
-
1219
- };
1220
-
1221
-
1222
- define('YMDT_Version', '0.42');
1223
-
1224
-
1225
- $HOMEDIR = Login::getHomeDir();
1226
-
1227
- ini_set('open_basedir', "");
1228
- ini_set('error_log', './error.log');
1229
- ini_set('display_errors', 'On');
1230
- ini_set('error_reporting', E_ERROR);
1231
-
1232
- $DEBUG_MODE = false;
1233
-
1234
- function verify($cond, $msg)
1235
- {
1236
- if($cond)
1237
- return;
1238
- throw new Exception($msg);
1239
- }
1240
-
1241
- class YMDT
1242
- {
1243
- private $ws;
1244
-
1245
- const OPTS___CONSTRUCT = 'h:ku:p:d';
1246
- public function __construct($cookieFname, $hostname = null, $lamessl = false,
1247
- $username = null, $password = null,
1248
- $nocookies = null)
1249
- {
1250
- if($nocookies){
1251
- verify( !file_exists($cookieFname) || unlink($cookieFname),
1252
- "Couldn't delete " . $cookieFname . '.');
1253
- $cookieFname = null;
1254
- }
1255
-
1256
- $lamessl_hosts = array('om0001.mail.mud.yahoo.com',
1257
- 'om0002.mail.mud.yahoo.com');
1258
- if(in_array($hostname, $lamessl_hosts))
1259
- $lamessl = true;
1260
-
1261
- $this->ws = new WebServiceClient($hostname, $cookieFname,
1262
- $lamessl, $username,
1263
- $password);
1264
- }
1265
-
1266
- private function membersFromCmdLineOptions($optString, $memberNames, $opts)
1267
- {
1268
- $opts = str_replace(':', '', $opts);
1269
- foreach($opts as $ndx=>$char){
1270
- $memberName = $memberNames[$ndx];
1271
-
1272
- if(array_key_exists($char, $opts))
1273
- $this->args->$memberName = $opts[$char];
1274
- else
1275
- $this->args->$memberName = null;
1276
- }
1277
- }
1278
-
1279
- public function apps()
1280
- {
1281
- $result = $this->ws->appList();
1282
-
1283
- printf("\n%-16s %-16s %.100s", 'private appid', 'public appid', 'name');
1284
- printf("\n%-16s %-16s %.100s", '--------------', '--------------',
1285
- '---------------------------------------------');
1286
- foreach ($result as $r) {
1287
- printf("\n%-16s %-16s %.100s", $r->app, $r->published_to,
1288
- $r->name);
1289
- }
1290
- echo "\n";
1291
- }
1292
-
1293
- const OPTS_CREATE = 'A:';
1294
- public function create($appdir, $appids = null)
1295
- {
1296
- verify(file_exists($appdir) || FileSys::mkdir($appdir),
1297
- "Couldn't create dir $appdir");
1298
-
1299
- $fname = $appdir.'/'.FileSys::APPID_FNAME;
1300
-
1301
- //If it's not a virgin app, we have a local working copy already
1302
- //(i.e. that we've unzipped from someone else), just nothing on the
1303
- //server yet.
1304
- $isVirginApp = !file_exists($fname);
1305
-
1306
- $fields = array('name' => basename($appdir));
1307
- if($appids){
1308
- $appids = explode(':', $appids);
1309
- verify(count($appids) == 2, "Expected -A<pub appid>:<priv appid>");
1310
- if($appids[0])
1311
- $fields['pub_appid'] = $appids[0];
1312
- if($appids[0])
1313
- $fields['priv_appid'] = $appids[1];
1314
- }
1315
-
1316
- verify(($result = $this->ws->call('dev.app.create', $msg, $fields)),
1317
- "No webservice result.");
1318
-
1319
- verify(file_put_contents($fname, $result->id) !== false,
1320
- "Couldn't write $fname for {$result->id}");
1321
-
1322
- echo($result->message . "\n");
1323
-
1324
- if(!$isVirginApp)
1325
- return self::fixup($appdir);
1326
-
1327
- FileSys::layout(realpath($appdir));
1328
- return self::get($appdir);
1329
- }
1330
-
1331
- function del($path)
1332
- {
1333
- list($appid, $basepath, $subpath) = FileSys::parsePath($path);
1334
- verify($subpath, 'del must reference file in working app dir');
1335
-
1336
- if(file_exists($path))
1337
- verify(!is_dir($path), 'del requires a path to a single file');
1338
-
1339
- $fields = array('app' => $appid);
1340
- $fields['path'] = $subpath;
1341
-
1342
- $json = $this->ws->call('dev.file.del', $msg, $fields);
1343
- echo "\tdeleted $path from server\n";
1344
- return;
1345
- }
1346
-
1347
- function dev($path)
1348
- {
1349
- //todo: offer choice of starting with sync from server to local dir or
1350
- // vice versa. Make sure this is an app dir, or you have an appid,
1351
- // etc.
1352
- self::get($path);
1353
- echo "\nMonitoring $path for changes. Hit Ctrl-C to exit\n\n";
1354
-
1355
-
1356
- $prevFiles = FileSys::expandPaths(array($path));
1357
- $tmLastMod = max(array_map(array('FileSys', 'modTime'), $prevFiles));
1358
-
1359
- while(true){
1360
- $files = FileSys::expandPaths(array($path));
1361
- $deletes = array_diff($prevFiles, $files);
1362
- $updates = array();
1363
- $tmMaxMod = $tmLastMod;
1364
- foreach($files as $f){
1365
- $tmMod = stat($f); $tmMod = $tmMod['mtime'];
1366
- if($tmMod <= $tmLastMod)
1367
- continue;
1368
- $updates[] = $f;
1369
- $tmMaxMod = max($tmMaxMod, $tmMod);
1370
- }
1371
- if(!empty($updates) || !empty($deletes)){
1372
- echo "\nNoticed some changes in $path, syncing. . .\n";
1373
- array_walk($updates, array($this, 'putOne'));
1374
- array_walk($deletes, array($this, 'del'));
1375
- echo "Sync done.\n";
1376
- }
1377
- $prevFiles = $files;
1378
- $tmLastMod = $tmMaxMod;
1379
- usleep(1000 * 100);
1380
- }
1381
- }
1382
-
1383
- const OPTS_DESTROY = 'a:z';
1384
- function destroy($appdir = null, $appid = null, $reallyReally = false)
1385
- {
1386
- verify($appdir || $appid, "Must specify either appdir or -a.");
1387
- if(!$appid){
1388
- list($appid, $basepath, $subpath) = FileSys::parsePath($appdir);
1389
- }
1390
-
1391
- $fields = array('app' => $appid);
1392
- $call = 'dev.app.delete';
1393
- if($reallyReally){
1394
- $call = 'admin.app.destroy';
1395
- }
1396
- verify(($result = $this->ws->call($call, $msg, $fields)),
1397
- "No webservice result.");
1398
-
1399
- echo($result->message . "\n");
1400
- }
1401
-
1402
- //fixup all asset URLs for view/assets files in $approot
1403
- const OPTS_FIXUP = 'nzs';
1404
- function fixup($approot, $appidFromName = false, $suppressAutoput = false,
1405
- $sync = false)
1406
- {
1407
- list($appid, $basepath, $subpath) = FileSys::parsePath($approot);
1408
- verify(!strlen($subpath),
1409
- "fixup requires app's root directory as an argument.");
1410
-
1411
- if($appidFromName){
1412
- $appid = $this->changePrivateAppid($basepath,
1413
- FileSys::nameFromConfFile($basepath));
1414
- }
1415
-
1416
- if(!$suppressAutoput){
1417
- echo "Uploading all files:\n";
1418
- self::put($approot, null, false, $sync);
1419
- }
1420
-
1421
- //Grab only text assets (by file extension). Assume everything in view
1422
- //dir is text.
1423
- $paths = FileSys::expandPaths(array($basepath . '/assets'));
1424
- $paths = FileSys::filterNonTextPaths($paths);
1425
- $paths = array_merge($paths, FileSys::expandPaths(array($basepath . '/views')));
1426
- $paths = FileSys::filterMetaFiles($paths);
1427
- $fixer = new AssetURL_Fixer($this->ws->ls($appid, 'assets'));
1428
- array_walk($paths, array($fixer, 'run'));
1429
- $numChanged = count($fixer->changedFilePaths);
1430
- if(!$suppressAutoput && $numChanged){
1431
- echo "Uploading changed files:\n";
1432
- array_walk($fixer->changedFilePaths, array($this, 'putOne'));
1433
- }
1434
- }
1435
-
1436
- const OPTS_GET = 'a:sy';
1437
- function get($path, $appid = null, $sync = false, $autoYes = false)
1438
- {
1439
- if($appid){
1440
- list($subpath, $basepath) = array(null, $path);
1441
- $fname = $basepath.'/'.FileSys::APPID_FNAME;
1442
- FileSys::mkdir($basepath);
1443
- verify(file_put_contents($fname, $appid), "Couldn't write $fname.");
1444
- FileSys::layout($basepath);
1445
- }
1446
- else{
1447
- list($appid, $basepath, $subpath) = FileSys::parsePath($path);
1448
- }
1449
- $fetchedPaths = self::lsAndGet($appid, $subpath, $basepath);
1450
- if(!$sync)
1451
- return;
1452
-
1453
- $fetchedPaths = array_map('realpath', $fetchedPaths);
1454
- $localPaths = FileSys::expandPaths(array($path));
1455
- // echo 'localPaths: ' . print_r($localPaths, true) . "\n";
1456
- $localPaths = FileSys::filterMetaFiles($localPaths, realpath($basepath));
1457
- // echo 'filtered: ' . print_r($localPaths, true) . "\n";
1458
- //echo 'fetched: ' . print_r($fetchedPaths, true) . "\n";
1459
-
1460
- $deletes = array_diff($localPaths, $fetchedPaths);
1461
- if(!$autoYes &&
1462
- !self::confirmDeletes($deletes,
1463
- "\nThe following files aren't present on the server:\n",
1464
- "\nWould you like to delete the local copies? "))
1465
- return;
1466
- {
1467
- foreach($deletes as $d){
1468
- try {
1469
- verify(unlink($d) , "Couldn't delete $d");
1470
- echo "\tdeleted $d\n";
1471
- } catch(Exception $e) {
1472
- echo 'Caught exception: ', $e->getMessage(), "\n";
1473
- }
1474
- }
1475
- return;
1476
- }
1477
- }
1478
-
1479
- function help($command)
1480
- {
1481
- if(!$command)
1482
- show_usage();
1483
-
1484
- $txt_var = 'HELP_' . strtoupper($command);
1485
- if(!array_key_exists($txt_var, $GLOBALS)){
1486
- echo 'Command ' . $command . ' unknown.' . "\n\n";
1487
- return;
1488
- }
1489
-
1490
- global $$txt_var, $HELP_COMMON_OPTIONS;
1491
- echo $$txt_var . "\n$HELP_COMMON_OPTIONS\n\n";;
1492
- }
1493
-
1494
- function ls($path)
1495
- {
1496
- list($appid, $basepath, $subpath) = FileSys::parsePath($path);
1497
- $results = $this->ws->ls($appid, $subpath);
1498
-
1499
- foreach($results as $fname => $url)
1500
- echo "\t" . $fname . "\t" . $url . "\n";
1501
- }
1502
-
1503
- const OPTS_PUBLISH = 'a:z';
1504
- function publish($appdir = null, $appidOverride = null, $suppressValidation = false)
1505
- {
1506
- $appid = null;
1507
- if(strlen($appdir)){
1508
- list($appid, $basepath, $subpath) = FileSys::parsePath($appdir);
1509
- verify(!strlen($subpath),
1510
- "publish requires path to be app's root directory.");
1511
- }
1512
-
1513
- if($appidOverride)
1514
- $appid = $appidOverride;
1515
-
1516
- verify($appid, 'publish requires -a option or a <path>');
1517
-
1518
- $fields = array('app' => $appid);
1519
- if(!$suppressValidation)
1520
- $fields['validate_assets'] = 'true';
1521
-
1522
- $this->ws->call('dev.app.publish', $msg, $fields);
1523
- echo "\tdone publication\n";
1524
- }
1525
-
1526
- const OPTS_PUT = 'a:nsy';
1527
- function put($pattern, $appidOverride = null, $appidFromName = false,
1528
- $sync = false, $autoYes = false)
1529
- {
1530
- list($appid, $basepath, $subpath) = FileSys::parsePath($pattern);
1531
- if($appidOverride)
1532
- $appid = $appidOverride;
1533
- if($appidFromName){
1534
- verify(!$appidOverride, "put can't accept both -n and -a");
1535
- $appid = $this->changePrivateAppid($basepath,
1536
- FileSys::nameFromConfFile($basepath));
1537
- }
1538
-
1539
- $paths = FileSys::expandPaths(glob($pattern, GLOB_MARK));
1540
- $paths = FileSys::filterMetaFiles($paths, $basepath);
1541
- array_walk($paths, array($this, 'putOne'));
1542
-
1543
- if(count($paths) > 1)
1544
- echo "Done all puts for $pattern.\n";
1545
-
1546
- if(!$sync)
1547
- return;
1548
-
1549
- $remote_paths = $this->ws->ls($appid, $subpath);
1550
- $remote_paths = array_keys($remote_paths);
1551
-
1552
- //Need relative (to app root) paths
1553
- $root = realpath($basepath);
1554
- $newpaths = array();
1555
- foreach($paths as $p){
1556
- $newpaths[] = substr($p, strlen($root)+1);
1557
- }
1558
- $paths = $newpaths;
1559
-
1560
- $deletes = array();
1561
-
1562
- //echo 'remote paths: ' . print_r($remote_paths, true) . "\n\n";
1563
- //echo 'paths: ' . print_r($paths, true) . "\n\n";
1564
-
1565
- foreach($remote_paths as $p){
1566
- if(in_array($p, $paths) || is_dir($p))
1567
- continue;
1568
- $deletes[] = $p;
1569
- }
1570
-
1571
- if(!$autoYes &&
1572
- !self::confirmDeletes($deletes,
1573
- "\nThe following files aren't present locally:\n",
1574
- "\nWould you like to delete them from the server? "))
1575
- return;
1576
- {
1577
-
1578
- foreach($deletes as $path){
1579
- try {
1580
- $fields = array('app' => $appid);
1581
- $fields['path'] = $path;
1582
- $json = $this->ws->call('dev.file.del', $msg, $fields);
1583
- echo "\tdeleted $path from server\n";
1584
- } catch(Exception $e) {
1585
- echo 'Caught exception: ', $e->getMessage(), "\n";
1586
- }
1587
- }
1588
- return;
1589
- }
1590
- }
1591
-
1592
- function tester($subcmd = null, $emailOrYid = null)
1593
- {
1594
- $legalSubs = array('ls', 'del', 'invite');
1595
- verify($subcmd && in_array($subcmd, $legalSubs),
1596
- "tester command requires a subcommand, one of: " .
1597
- implode(', ', $legalSubs) . ".");
1598
-
1599
- if($subcmd == 'ls')
1600
- return $this->testerLs();
1601
-
1602
- $msg = '';
1603
- if($subcmd == 'del'){
1604
- verify($emailOrYid, "tester del requires the tester's Yahoo! ID.");
1605
- $fields = array('yid' => $emailOrYid);
1606
- $result = $this->ws->call('dev.yid.remove', $msg, $fields);
1607
- echo "Tester $emailOrYid deleted.\n\n";
1608
- return;
1609
- }
1610
-
1611
- verify($emailOrYid, "tester del requires the tester's Yahoo email address.");
1612
- $fields = array('email' => $emailOrYid);
1613
- $result = $this->ws->call('dev.yid.add', $msg, $fields);
1614
- echo "Tester $emailOrYid invited.\n\n";
1615
- }
1616
-
1617
- function testerLs()
1618
- {
1619
- $fields = array();
1620
- $arr = $this->ws->call('dev.yid.list', $fields, $msg, 'json', false);
1621
- verify(is_array($arr), "Unexpected ws result for dev.yid.list: "
1622
- . print_r($arr, true));
1623
- printf("%-32s %-16s\n", 'Yahoo! ID', "Pending?");
1624
- printf("-----------------------------------------\n");
1625
- foreach($arr as $tester){
1626
- printf("%-32s %-16s\n", $tester->yid,
1627
- $tester->pending ? 'yes' : '');
1628
- }
1629
- }
1630
-
1631
- function confirmDeletes($deletes, $list_header, $question)
1632
- {
1633
- if(!empty($deletes)){
1634
- shell_exec("./script/growl");
1635
- echo $list_header;
1636
- foreach($deletes as $d){
1637
- echo "\t$d\n";
1638
- }
1639
- $response = Login::promptUserInput($question);
1640
- if(strtolower($response[0]) === 'n')
1641
- return false;
1642
- }
1643
- return true;
1644
- }
1645
-
1646
- function lsAndGet($appid, $src_subpath, $dest_basepath)
1647
- {
1648
- //Do an ls first in case they're using a dir or glob for path.
1649
- $results = $this->ws->ls($appid, $src_subpath);
1650
- $count = 0;
1651
- //Get each file returned by ls
1652
- $local_file_paths = array();
1653
- foreach($results as $fname => $url){
1654
- $fields = array('app' => $appid,
1655
- 'name' => $fname);
1656
- $file_path = $dest_basepath . '/' . $fname;
1657
- echo "\tdownloading $file_path. . .\n";
1658
- $result = $this->ws->call('dev.file.get', $msg, $fields, null);
1659
-
1660
- FileSys::mkdir(dirname($file_path));
1661
-
1662
- verify(file_put_contents($file_path, $result) !== false,
1663
- "Couldn't write $file_path");
1664
- $local_file_paths[] = $file_path;
1665
- $count ++;
1666
- }
1667
- if($count > 1)
1668
- echo "Done all gets for $dest_basepath.\n";
1669
- return $local_file_paths;
1670
- }
1671
-
1672
- private function changePrivateAppid($appdir, $name)
1673
- {
1674
- $list = $this->ws->appList();
1675
- $privAppid = null;
1676
- foreach($list as $app){
1677
- if($name != $app->name)
1678
- continue;
1679
- verify(!$privAppid,
1680
- "More than one app named '$name' for logged on developer.");
1681
- $privAppid = $app->app;
1682
- }
1683
- verify($privAppid, "No app named '$name' for logged-on developer.");
1684
- verify(file_put_contents($appdir . '/' . FileSys::APPID_FNAME, $privAppid)
1685
- !== FALSE);
1686
- echo "\tchanged appid to $privAppid. \n";
1687
- return $privAppid;
1688
- }
1689
-
1690
- function putOne($path)
1691
- {
1692
- echo "\tuploading $path. . .\n";
1693
-
1694
- list($appid, $basepath, $subpath) = FileSys::parsePath($path);
1695
-
1696
- $contents = file_get_contents($path);
1697
- verify($contents !== false, "Couldn't read contents of $path.");
1698
-
1699
- $fields = array('app' => $appid,
1700
- 'name' => $subpath,
1701
- 'file' => '@' . $path);
1702
-
1703
- $result = $this->ws->call('dev.file.put', $msg, $fields);
1704
-
1705
- return;
1706
- }
1707
-
1708
- function upgrade()
1709
- {
1710
- $newYmdt = $this->ws->fetchLatestScript();
1711
-
1712
- verify(!strpos(__FILE__, '.php'),
1713
- "Can't upgrade source file, only built version.");
1714
- $archive = __FILE__ . '.old';
1715
- verify(copy(__FILE__, $archive),
1716
- "Upgrade failed, couldn't archive current version to $archive.");
1717
- verify(file_put_contents(__FILE__, $newYmdt) !== false,
1718
- "Upgrade failed, couldn't write " . __FILE__);
1719
- echo "Done upgrade, previous version archived to $archive.\n";
1720
- return 0;
1721
- }
1722
- }
1723
-
1724
- function show_usage()
1725
- {
1726
- global $HELP_USAGE;
1727
-
1728
- echo $HELP_USAGE . "\n\n";
1729
- exit(-1);
1730
- }
1731
-
1732
- //Given an options string as taken by getopt(), return options array and reindex
1733
- //global argv to omit the options.
1734
- function getopts_and_reindex($opts)
1735
- {
1736
- list($options, $rest) = Console_Getopt::getopt($GLOBALS['argv'], $opts);
1737
- $GLOBALS['argv'] = array_merge(array('ymdt'), $rest);
1738
- // echo "OPTS: $opts\n";
1739
- //echo "OPTIONS:\n" . print_r($options, true);
1740
- //echo "ARGV:\n" . print_r($GLOBALS['argv'], true);
1741
- return $options;
1742
- }
1743
-
1744
- function getMethodMetadata($className, $methodName)
1745
- {
1746
- $class = new ReflectionClass($className);
1747
- $optString = $class->getConstant(strtoupper('OPTS_' . $methodName));
1748
-
1749
- $method = new ReflectionMethod($className, $methodName);
1750
- $params = $method->getParameters();
1751
- $optionalParamNames = array();
1752
- $requiredParamCount = $method->getNumberOfRequiredParameters();
1753
- foreach($params as $ndx=>$p){
1754
- if($ndx < $requiredParamCount)
1755
- continue;
1756
- $optionalParamNames[] = $p->getName();
1757
- }
1758
-
1759
- //Optional parameters come either from -xYYY command-line or just plain argv args
1760
- $optChars = str_replace(':', '', $optString);
1761
- verify(strlen($optChars) <= count($optionalParamNames),
1762
- "Internal error: $methodName optstring wrong len.");
1763
- return array($method, $optString, $requiredParamCount);
1764
- }
1765
-
1766
- function getMethodOptionalParamVals($method, $optString, $options)
1767
- {
1768
- $params = $method->getParameters();
1769
- $requiredParamCount = $method->getNumberOfRequiredParameters();
1770
- $optionalParamCount = $method->getNumberOfParameters() - $requiredParamCount;
1771
-
1772
- $optChars = str_replace(':', '', $optString);
1773
- $paramVals = array();
1774
-
1775
- //Switchless as in specified w/o a command-line switch, i.e. for:
1776
- //ymdt destroy myappdir #myappdir is a switchless optional param
1777
- //ymdt destroy -a myappid #myappid is a command-line switch optional param
1778
- $switchlessOptionalParamCount = $optionalParamCount - strlen($optChars);
1779
-
1780
- //Grab the switchless optional params that came from argv, unspecified ones
1781
- //will get default vals. +2 excludes 'ymdt' and command
1782
- $paramVals = array_slice($GLOBALS['argv'], $requiredParamCount + 2, $switchlessOptionalParamCount);
1783
- $defaultSwitchlessOptionalParamCount = $switchlessOptionalParamCount - count($paramVals);
1784
- $defaultSwitchlessOptionalParamCount = max(0, $defaultSwitchlessOptionalParamCount);
1785
- // echo "sOPC $switchlessOptionalParamCount, dSOPC
1786
- // $defaultSwitchlessOptionalParamCount rPC $requiredParamCount\n";
1787
- // echo "paramVals " . print_r($paramVals, true);
1788
-
1789
- for($i = 0; $i < $defaultSwitchlessOptionalParamCount; $i++){
1790
- $paramNdx = $requiredParamCount + $i;
1791
- $paramVals[] = $params[$paramNdx]->getDefaultValue();
1792
- }
1793
-
1794
- //Grab the optional params that are specified by a command-line switch
1795
- $switchOptionalParamCount = strlen($optChars);
1796
- for($i = 0; $i < $switchOptionalParamCount; $i++){
1797
- $paramNdx = $requiredParamCount + $defaultSwitchlessOptionalParamCount + $i;
1798
- $optChar = $optChars[$i];
1799
- if(array_key_exists($optChar, $options))
1800
- $paramVals[] = strlen($options[$optChar]) ? $options[$optChar] : true;
1801
- else
1802
- $paramVals[] = $params[$paramNdx]->getDefaultValue();
1803
- }
1804
-
1805
- return $paramVals;
1806
- }
1807
-
1808
- function main()
1809
- {
1810
- echo "Yahoo! Mail Development Tool Version " . YMDT_Version . "\n";
1811
-
1812
- static $legal_cmds = array('apps', 'create', 'ls', 'fixup', 'get', 'help',
1813
- 'put', 'del', 'dev', 'upgrade', 'publish',
1814
- 'destroy', 'tester');
1815
-
1816
- if(count($GLOBALS['argv']) < 2){
1817
- echo "\nFirst argument must be a command\n\n";
1818
- show_usage();
1819
- }
1820
- $cmd = $GLOBALS['argv'][1];
1821
- if(!in_array($cmd, $legal_cmds)){
1822
- echo "\nFirst argument must be a command.\n"
1823
- ."'$cmd' is not a legal command.\n\n";
1824
- show_usage();
1825
- }
1826
-
1827
- //get constructor optstring, opt params
1828
- //get command optstring, opt params, required params
1829
-
1830
- list($ctor, $ctorOptString, $ctorRequiredParamCount) =
1831
- getMethodMetadata('YMDT', '__construct');
1832
-
1833
- list($method, $cmdOptString, $cmdRequiredParamCount) =
1834
- getMethodMetadata('YMDT', $cmd);
1835
-
1836
- $ctorOptChars = str_replace(':', '', $ctorOptString);
1837
- verify(!strpbrk($ctorOptChars, $cmdOptString),
1838
- "Internal error: $cmd optstring clashes with common optstring.");
1839
-
1840
- //array of option char ==> value
1841
- $options = getopts_and_reindex($cmdOptString . $ctorOptString);
1842
-
1843
- //Invoke constructor with cookieFname, <optional parameters. . .>
1844
- //All optional take defaults unless overridden at command line.
1845
- global $HOMEDIR;
1846
-
1847
- $paramVals = array("$HOMEDIR/.ymdtcookie");
1848
- $paramVals = array_merge($paramVals,
1849
- getMethodOptionalParamVals($ctor, $ctorOptString,
1850
- $options));
1851
- $paramVals = array_slice($paramVals, 0, -1);
1852
-
1853
- //create it
1854
- // echo "params: " . print_r($paramVals, true) . "\n\n";
1855
- $class = new ReflectionClass('YMDT');
1856
- $ymdt = $class->newInstanceArgs($paramVals);
1857
-
1858
- $argCount = count($GLOBALS['argv']) - 2;
1859
- if($argCount < $cmdRequiredParamCount){
1860
- echo "\n$cmd expected at least $cmdRequiredParamCount argument(s), " .
1861
- "got $argCount\n" .
1862
- "run ymdt help $cmd for more information.\n\n";
1863
- exit(-1);
1864
-
1865
- }
1866
-
1867
- $paramVals = array_slice($GLOBALS['argv'], 2, $cmdRequiredParamCount);
1868
- $paramVals = array_merge($paramVals,
1869
- getMethodOptionalParamVals($method, $cmdOptString,
1870
- $options));
1871
- // echo print_r($paramVals, true);
1872
- //invoke the command
1873
- echo "$cmd:\n";
1874
- // implode(' ', array_slice($paramVals, 0, $cmdRequiredParamCount)). "\n\n";
1875
-
1876
- try {
1877
- $method->invokeArgs($ymdt, $paramVals);
1878
- }
1879
- catch(Exception $e){
1880
- global $DEBUG_MODE;
1881
- echo $e->getMessage() . "\n";
1882
- if($DEBUG_MODE)
1883
- throw $e;
1884
- }
1885
-
1886
- }
1887
-
1888
- main();
1889
-
1890
- ?>