@@ -2,6 +2,13 @@ package programmer
2
2
3
3
import (
4
4
"bufio"
5
+ "bytes"
6
+ "fmt"
7
+ "io"
8
+ "log"
9
+ "mime/multipart"
10
+ "net/http"
11
+ "os"
5
12
"os/exec"
6
13
"path/filepath"
7
14
"regexp"
@@ -13,6 +20,7 @@ import (
13
20
"github.com/facchinm/go-serial"
14
21
shellwords "github.com/mattn/go-shellwords"
15
22
"github.com/pkg/errors"
23
+ "github.com/sfreiberg/simplessh"
16
24
)
17
25
18
26
type logger interface {
@@ -84,19 +92,27 @@ func Resolve(port, board, file, commandline string, extra Extra, t locater) (str
84
92
return commandline , nil
85
93
}
86
94
87
- // Do performs a command on a port with a board attached to it
88
- func Do (port , commandline string , extra Extra , l logger ) error {
89
- if extra .Network {
90
- doNetwork ()
91
- } else {
92
- return doSerial (port , commandline , extra , l )
95
+ // Network performs a network upload
96
+ func Network (port , board , file , commandline string , auth Auth , l logger ) error {
97
+ // Defaults
98
+ if auth .Username == "" {
99
+ auth .Username = "root"
100
+ }
101
+ if auth .Password == "" {
102
+ auth .Password = "arduino"
93
103
}
94
- return nil
95
- }
96
104
97
- func doNetwork () {}
105
+ // try with a form
106
+ err := form (port , board , file , auth , l )
107
+ if err != nil {
108
+ // try with ssh
109
+ err = ssh (port , file , commandline , auth , l )
110
+ }
111
+ return err
112
+ }
98
113
99
- func doSerial (port , commandline string , extra Extra , l logger ) error {
114
+ // Serial performs a serial upload
115
+ func Serial (port , commandline string , extra Extra , l logger ) error {
100
116
// some boards needs to be resetted
101
117
if extra .Use1200bpsTouch {
102
118
var err error
@@ -260,3 +276,138 @@ func program(binary string, args []string, l logger) error {
260
276
261
277
return errors .Wrapf (err , "Executing command" )
262
278
}
279
+
280
+ func form (port , board , file string , auth Auth , l logger ) error {
281
+ // Prepare a form that you will submit to that URL.
282
+ _url := "http://" + port + "/data/upload_sketch_silent"
283
+ var b bytes.Buffer
284
+ w := multipart .NewWriter (& b )
285
+
286
+ // Add your image file
287
+ file = strings .Trim (file , "\n " )
288
+ f , err := os .Open (file )
289
+ if err != nil {
290
+ return errors .Wrapf (err , "Open file %s" , file )
291
+ }
292
+ fw , err := w .CreateFormFile ("sketch_hex" , file )
293
+ if err != nil {
294
+ return errors .Wrapf (err , "Create form file" )
295
+ }
296
+ if _ , err = io .Copy (fw , f ); err != nil {
297
+ return errors .Wrapf (err , "Copy form file" )
298
+ }
299
+ // Add the other fields
300
+ board = strings .Replace (board , ":" , "_" , - 1 )
301
+ if fw , err = w .CreateFormField ("board" ); err != nil {
302
+ return errors .Wrapf (err , "Create board field" )
303
+ }
304
+ if _ , err = fw .Write ([]byte (board )); err != nil {
305
+ return errors .Wrapf (err , "" )
306
+ }
307
+ // Don't forget to close the multipart writer.
308
+ // If you don't close it, your request will be missing the terminating boundary.
309
+ w .Close ()
310
+
311
+ // Now that you have a form, you can submit it to your handler.
312
+ req , err := http .NewRequest ("POST" , _url , & b )
313
+ if err != nil {
314
+ return errors .Wrapf (err , "Create POST req" )
315
+ }
316
+
317
+ // Don't forget to set the content type, this will contain the boundary.
318
+ req .Header .Set ("Content-Type" , w .FormDataContentType ())
319
+ if auth .Username != "" {
320
+ req .SetBasicAuth (auth .Username , auth .Password )
321
+ }
322
+
323
+ info (l , "Network upload on " , port )
324
+
325
+ // Submit the request
326
+ client := & http.Client {}
327
+ res , err := client .Do (req )
328
+ if err != nil {
329
+ log .Println ("Error during post request" )
330
+ return errors .Wrapf (err , "" )
331
+ }
332
+
333
+ // Check the response
334
+ if res .StatusCode != http .StatusOK {
335
+ return errors .Wrapf (err , "Bad status: %s" , res .Status )
336
+ }
337
+ return nil
338
+ }
339
+
340
+ func ssh (port , file , commandline string , auth Auth , l logger ) error {
341
+ // Connect via ssh
342
+ client , err := simplessh .ConnectWithPassword (port + ":22" , auth .Username , auth .Password )
343
+ debug (l , "Connect via ssh " , client , err )
344
+ if err != nil {
345
+ return errors .Wrapf (err , "Connect via ssh" )
346
+ }
347
+ defer client .Close ()
348
+
349
+ // Copy the sketch
350
+ err = scp (client , file , "/tmp/sketch" + filepath .Ext (file ))
351
+ debug (l , "Copy the sketch " , err )
352
+ if err != nil {
353
+ return errors .Wrapf (err , "Copy sketch" )
354
+ }
355
+
356
+ // very special case for Yun (remove once AVR boards.txt is fixed)
357
+ if commandline == "" {
358
+ commandline = "merge-sketch-with-bootloader.lua /tmp/sketch.hex && /usr/bin/run-avrdude /tmp/sketch.hex"
359
+ }
360
+
361
+ // Execute commandline
362
+ output , err := client .Exec (commandline )
363
+ debug (l , "Execute commandline " , commandline , output , err )
364
+ if err != nil {
365
+ return errors .Wrapf (err , "Execute commandline" )
366
+ }
367
+ return nil
368
+ }
369
+
370
+ // scp uploads sourceFile to remote machine like native scp console app.
371
+ func scp (client * simplessh.Client , sourceFile , targetFile string ) error {
372
+ // open ssh session
373
+ session , err := client .SSHClient .NewSession ()
374
+ if err != nil {
375
+ return errors .Wrapf (err , "open ssh session" )
376
+ }
377
+ defer session .Close ()
378
+
379
+ // open file
380
+ src , err := os .Open (sourceFile )
381
+ if err != nil {
382
+ return errors .Wrapf (err , "open file %s" , sourceFile )
383
+ }
384
+
385
+ // stat file
386
+ srcStat , err := src .Stat ()
387
+ if err != nil {
388
+ return errors .Wrapf (err , "stat file %s" , sourceFile )
389
+ }
390
+
391
+ // Copy over ssh
392
+ go func () {
393
+ w , _ := session .StdinPipe ()
394
+
395
+ fmt .Fprintln (w , "C0644" , srcStat .Size (), filepath .Base (targetFile ))
396
+
397
+ if srcStat .Size () > 0 {
398
+ io .Copy (w , src )
399
+ fmt .Fprint (w , "\x00 " )
400
+ w .Close ()
401
+ } else {
402
+ fmt .Fprint (w , "\x00 " )
403
+ w .Close ()
404
+ }
405
+
406
+ }()
407
+
408
+ if err := session .Run ("scp -t " + targetFile ); err != nil {
409
+ return errors .Wrapf (err , "Execute %s" , "scp -t " + targetFile )
410
+ }
411
+
412
+ return nil
413
+ }
0 commit comments