Subversion Repositories Programming Utils

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
86 rm5248 1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one
3
 * or more contributor license agreements.  See the NOTICE file
4
 * distributed with this work for additional information
5
 * regarding copyright ownership.  The ASF licenses this file
6
 * to you under the Apache License, Version 2.0 (the
7
 * "License"); you may not use this file except in compliance
8
 * with the License.  You may obtain a copy of the License at
9
 *
10
 *   http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing,
13
 * software distributed under the License is distributed on an
14
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
 * KIND, either express or implied.  See the License for the
16
 * specific language governing permissions and limitations
17
 * under the License.
18
 */
19
package org.apache.sshd;
20
 
21
import java.io.ByteArrayOutputStream;
22
import java.io.File;
23
import java.io.FileOutputStream;
24
import java.io.IOException;
25
import java.io.InputStream;
26
import java.io.OutputStream;
27
import java.util.Properties;
28
import java.util.concurrent.TimeUnit;
29
 
30
import ch.ethz.ssh2.Connection;
31
import ch.ethz.ssh2.SCPClient;
32
import com.jcraft.jsch.ChannelExec;
33
import com.jcraft.jsch.JSch;
34
import com.jcraft.jsch.JSchException;
35
import org.apache.sshd.client.ScpClient;
36
import org.apache.sshd.server.command.ScpCommandFactory;
37
import org.apache.sshd.util.BaseTest;
38
import org.apache.sshd.util.BogusPasswordAuthenticator;
39
import org.apache.sshd.util.EchoShellFactory;
40
import org.apache.sshd.util.JSchLogger;
41
import org.apache.sshd.util.SimpleUserInfo;
42
import org.apache.sshd.util.Utils;
43
import org.junit.After;
44
import org.junit.Before;
45
import org.junit.Ignore;
46
import org.junit.Test;
47
 
48
import static org.junit.Assert.assertEquals;
49
import static org.junit.Assert.assertFalse;
50
import static org.junit.Assert.assertTrue;
51
import static org.junit.Assert.fail;
52
 
53
/**
54
 * Test for SCP support.
55
 *
56
 * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
57
 */
58
public class ScpTest extends BaseTest {
59
 
60
    private SshServer sshd;
61
    private int port;
62
    private com.jcraft.jsch.Session session;
63
 
64
    @Before
65
    public void setUp() throws Exception {
66
        port = Utils.getFreePort();
67
//        port = 8102;
68
 
69
        sshd = SshServer.setUpDefaultServer();
70
        sshd.setPort(port);
71
        sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
72
        sshd.setCommandFactory(new ScpCommandFactory());
73
        sshd.setShellFactory(new EchoShellFactory());
74
        sshd.setPasswordAuthenticator(new BogusPasswordAuthenticator());
75
        sshd.start();
76
    }
77
 
78
    protected com.jcraft.jsch.Session getJschSession() throws JSchException {
79
        JSchLogger.init();
80
        JSch sch = new JSch();
81
        session = sch.getSession("sshd", "localhost", port);
82
        session.setUserInfo(new SimpleUserInfo("sshd"));
83
        session.connect();
84
        return session;
85
    }
86
 
87
    @After
88
    public void tearDown() throws Exception {
89
        if (session != null) {
90
            session.disconnect();
91
        }
92
        sshd.stop(true);
93
    }
94
 
95
    @Test
96
    @Ignore
97
    public void testExternal() throws Exception {
98
        System.out.println("Scp available on port " + port);
99
        Thread.sleep(5 * 60000);
100
    }
101
 
102
    @Test
103
    public void testUploadAbsoluteDriveLetter() throws Exception {
104
        SshClient client = SshClient.setUpDefaultClient();
105
        client.start();
106
        ClientSession session = client.connect("test", "localhost", port).await().getSession();
107
        session.addPasswordIdentity("test");
108
        session.auth().verify();
109
 
110
        ScpClient scp = session.createScpClient();
111
 
112
        String data = "0123456789\n";
113
 
114
        File root = new File("target/scp");
115
        Utils.deleteRecursive(root);
116
        root.mkdirs();
117
        new File(root, "local").mkdirs();
118
        assertTrue(root.exists());
119
 
120
 
121
        writeFile(new File("target/scp/local/out.txt"), data);
122
        new File(root, "remote").mkdirs();
123
        scp.upload(new File("target/scp/local/out.txt").getAbsolutePath(), "/" + new File("target/scp/remote/out.txt").getAbsolutePath().replace(File.separatorChar, '/'));
124
        assertFileLength(new File("target/scp/remote/out.txt"), data.length(), 5000);
125
        scp.upload(new File("target/scp/local/out.txt").getAbsolutePath(), new File("target/scp/remote/out2.txt").getAbsolutePath());
126
        assertFileLength(new File("target/scp/remote/out2.txt"), data.length(), 5000);
127
    }
128
 
129
    @Test
130
    public void testScpNativeOnSingleFile() throws Exception {
131
        SshClient client = SshClient.setUpDefaultClient();
132
        client.start();
133
        ClientSession session = client.connect("test", "localhost", port).await().getSession();
134
        session.addPasswordIdentity("test");
135
        session.auth().verify();
136
 
137
        ScpClient scp = session.createScpClient();
138
 
139
        String data = "0123456789\n";
140
 
141
        File root = new File("target/scp");
142
        Utils.deleteRecursive(root);
143
        root.mkdirs();
144
        new File(root, "local").mkdirs();
145
        assertTrue(root.exists());
146
 
147
 
148
        writeFile(new File("target/scp/local/out.txt"), data);
149
        try {
150
            scp.upload("target/scp/local/out.txt", "target/scp/remote/out.txt");
151
            fail("Expected IOException");
152
        } catch (IOException e) {
153
            // ok
154
        }
155
        new File(root, "remote").mkdirs();
156
        scp.upload("target/scp/local/out.txt", "target/scp/remote/out.txt");
157
        assertFileLength(new File("target/scp/remote/out.txt"), data.length(), 5000);
158
 
159
        scp.download("target/scp/remote/out.txt", "target/scp/local/out2.txt");
160
        assertFileLength(new File("target/scp/local/out2.txt"), data.length(), 5000);
161
 
162
        session.close(false).await();
163
        client.stop();
164
    }
165
 
166
    @Test
167
    public void testScpNativeOnMultipleFiles() throws Exception {
168
        SshClient client = SshClient.setUpDefaultClient();
169
        client.start();
170
        ClientSession session = client.connect("test", "localhost", port).await().getSession();
171
        session.addPasswordIdentity("test");
172
        session.auth().verify();
173
 
174
        ScpClient scp = session.createScpClient();
175
 
176
        String data = "0123456789\n";
177
 
178
        File root = new File("target/scp");
179
        Utils.deleteRecursive(root);
180
        root.mkdirs();
181
        new File(root, "local").mkdirs();
182
        new File(root, "remote").mkdirs();
183
        assertTrue(root.exists());
184
 
185
 
186
        writeFile(new File("target/scp/local/out1.txt"), data);
187
        writeFile(new File("target/scp/local/out2.txt"), data);
188
        try {
189
            scp.upload(new String[] { "target/scp/local/out1.txt", "target/scp/local/out2.txt" }, "target/scp/remote/out.txt");
190
            fail("Expected IOException");
191
        } catch (IOException e) {
192
            // Ok
193
        }
194
        writeFile(new File("target/scp/remote/out.txt"), data);
195
        try {
196
            scp.upload(new String[] { "target/scp/local/out1.txt", "target/scp/local/out2.txt" }, "target/scp/remote/out.txt");
197
            fail("Expected IOException");
198
        } catch (IOException e) {
199
            // Ok
200
        }
201
        new File(root, "remote/dir").mkdirs();
202
        scp.upload(new String[] { "target/scp/local/out1.txt", "target/scp/local/out2.txt" }, "target/scp/remote/dir");
203
        assertFileLength(new File("target/scp/remote/dir/out1.txt"), data.length(), 5000);
204
        assertFileLength(new File("target/scp/remote/dir/out2.txt"), data.length(), 5000);
205
 
206
        try {
207
            scp.download(new String[] { "target/scp/remote/dir/out1.txt", "target/scp/remote/dir/out2.txt" }, "target/scp/local/out1.txt");
208
            fail("Expected IOException");
209
        } catch (IOException e) {
210
            // Ok
211
        }
212
        try {
213
            scp.download(new String[] { "target/scp/remote/dir/out1.txt", "target/scp/remote/dir/out2.txt" }, "target/scp/local/dir");
214
            fail("Expected IOException");
215
        } catch (IOException e) {
216
            // Ok
217
        }
218
        new File(root, "local/dir").mkdirs();
219
        scp.download(new String[] { "target/scp/remote/dir/out1.txt", "target/scp/remote/dir/out2.txt" }, "target/scp/local/dir");
220
        assertFileLength(new File("target/scp/local/dir/out1.txt"), data.length(), 5000);
221
        assertFileLength(new File("target/scp/local/dir/out2.txt"), data.length(), 5000);
222
 
223
        session.close(false).await();
224
        client.stop();
225
    }
226
 
227
    @Test
228
    public void testScpNativeOnRecursiveDirs() throws Exception {
229
        SshClient client = SshClient.setUpDefaultClient();
230
        client.start();
231
        ClientSession session = client.connect("test", "localhost", port).await().getSession();
232
        session.addPasswordIdentity("test");
233
        session.auth().verify();
234
 
235
        ScpClient scp = session.createScpClient();
236
 
237
        String data = "0123456789\n";
238
 
239
        File root = new File("target/scp");
240
        Utils.deleteRecursive(root);
241
        root.mkdirs();
242
        new File(root, "local").mkdirs();
243
        new File(root, "remote").mkdirs();
244
        assertTrue(root.exists());
245
 
246
        new File("target/scp/local/dir").mkdirs();
247
        writeFile(new File("target/scp/local/dir/out1.txt"), data);
248
        writeFile(new File("target/scp/local/dir/out2.txt"), data);
249
        scp.upload("target/scp/local/dir", "target/scp/remote/", ScpClient.Option.Recursive);
250
        assertFileLength(new File("target/scp/remote/dir/out1.txt"), data.length(), 5000);
251
        assertFileLength(new File("target/scp/remote/dir/out2.txt"), data.length(), 5000);
252
 
253
        Utils.deleteRecursive(new File("target/scp/local/dir"));
254
        scp.download("target/scp/remote/dir", "target/scp/local", ScpClient.Option.Recursive);
255
        assertFileLength(new File("target/scp/local/dir/out1.txt"), data.length(), 5000);
256
        assertFileLength(new File("target/scp/local/dir/out2.txt"), data.length(), 5000);
257
 
258
        session.close(false).await();
259
        client.stop();
260
    }
261
 
262
    @Test
263
    public void testScpNativeOnDirWithPattern() throws Exception {
264
        SshClient client = SshClient.setUpDefaultClient();
265
        client.start();
266
        ClientSession session = client.connect("test", "localhost", port).await().getSession();
267
        session.addPasswordIdentity("test");
268
        session.auth().verify();
269
 
270
        ScpClient scp = session.createScpClient();
271
 
272
        String data = "0123456789\n";
273
 
274
        File root = new File("target/scp");
275
        Utils.deleteRecursive(root);
276
        root.mkdirs();
277
        new File(root, "local").mkdirs();
278
        new File(root, "remote").mkdirs();
279
        assertTrue(root.exists());
280
 
281
        writeFile(new File("target/scp/local/out1.txt"), data);
282
        writeFile(new File("target/scp/local/out2.txt"), data);
283
        scp.upload("target/scp/local/*", "target/scp/remote/");
284
        assertFileLength(new File("target/scp/remote/out1.txt"), data.length(), 5000);
285
        assertFileLength(new File("target/scp/remote/out2.txt"), data.length(), 5000);
286
 
287
        new File("target/scp/local/out1.txt").delete();
288
        new File("target/scp/local/out2.txt").delete();
289
        scp.download("target/scp/remote/*", "target/scp/local");
290
        assertFileLength(new File("target/scp/local/out1.txt"), data.length(), 5000);
291
        assertFileLength(new File("target/scp/local/out2.txt"), data.length(), 5000);
292
 
293
        session.close(false).await();
294
        client.stop();
295
    }
296
 
297
    @Test
298
    public void testScpNativeOnMixedDirAndFiles() throws Exception {
299
        SshClient client = SshClient.setUpDefaultClient();
300
        client.start();
301
        ClientSession session = client.connect("test", "localhost", port).await().getSession();
302
        session.addPasswordIdentity("test");
303
        session.auth().verify();
304
 
305
        ScpClient scp = session.createScpClient();
306
 
307
        String data = "0123456789\n";
308
 
309
        File root = new File("target/scp");
310
        Utils.deleteRecursive(root);
311
        root.mkdirs();
312
        new File(root, "local").mkdirs();
313
        new File(root, "remote").mkdirs();
314
        assertTrue(root.exists());
315
 
316
        new File("target/scp/local/dir").mkdirs();
317
        writeFile(new File("target/scp/local/out1.txt"), data);
318
        writeFile(new File("target/scp/local/dir/out2.txt"), data);
319
        scp.upload("target/scp/local/*", "target/scp/remote/", ScpClient.Option.Recursive);
320
        assertFileLength(new File("target/scp/remote/out1.txt"), data.length(), 5000);
321
        assertFileLength(new File("target/scp/remote/dir/out2.txt"), data.length(), 5000);
322
 
323
        Utils.deleteRecursive(new File("target/scp/local/out1.txt"));
324
        Utils.deleteRecursive(new File("target/scp/local/dir"));
325
        scp.download("target/scp/remote/*", "target/scp/local");
326
        assertFileLength(new File("target/scp/local/out1.txt"), data.length(), 5000);
327
        assertFalse(new File("target/scp/local/dir/out2.txt").exists());
328
 
329
        Utils.deleteRecursive(new File("target/scp/local/out1.txt"));
330
        scp.download("target/scp/remote/*", "target/scp/local", ScpClient.Option.Recursive);
331
        assertFileLength(new File("target/scp/local/out1.txt"), data.length(), 5000);
332
        assertFileLength(new File("target/scp/local/dir/out2.txt"), data.length(), 5000);
333
 
334
        session.close(false).await();
335
        client.stop();
336
    }
337
 
338
    @Test
339
    public void testScpNativePreserveAttributes() throws Exception {
340
        // Ignore this test if running a Windows system
341
        if (System.getProperty("os.name").toLowerCase().contains("win")) {
342
            return;
343
        }
344
 
345
        SshClient client = SshClient.setUpDefaultClient();
346
        client.start();
347
        ClientSession session = client.connect("test", "localhost", port).await().getSession();
348
        session.addPasswordIdentity("test");
349
        session.auth().verify();
350
 
351
        ScpClient scp = session.createScpClient();
352
 
353
        String data = "0123456789\n";
354
 
355
        File root = new File("target/scp");
356
        Utils.deleteRecursive(root);
357
        root.mkdirs();
358
        new File(root, "local").mkdirs();
359
        new File(root, "remote").mkdirs();
360
        assertTrue(root.exists());
361
 
362
        new File("target/scp/local/dir").mkdirs();
363
        long lastMod = new File("target/scp/local/dir").lastModified() - TimeUnit.DAYS.toMillis(1);
364
 
365
        writeFile(new File("target/scp/local/out1.txt"), data);
366
        writeFile(new File("target/scp/local/dir/out2.txt"), data);
367
        new File("target/scp/local/out1.txt").setLastModified(lastMod);
368
        new File("target/scp/local/out1.txt").setExecutable(true, true);
369
        new File("target/scp/local/out1.txt").setWritable(false, false);
370
        new File("target/scp/local/dir/out2.txt").setLastModified(lastMod);
371
        scp.upload("target/scp/local/*", "target/scp/remote/", ScpClient.Option.Recursive, ScpClient.Option.PreserveAttributes);
372
        assertFileLength(new File("target/scp/remote/out1.txt"), data.length(), 5000);
373
        assertEquals(lastMod, new File("target/scp/remote/out1.txt").lastModified());
374
        assertFileLength(new File("target/scp/remote/dir/out2.txt"), data.length(), 5000);
375
        assertEquals(lastMod, new File("target/scp/remote/dir/out2.txt").lastModified());
376
 
377
        Utils.deleteRecursive(new File("target/scp/local"));
378
        new File("target/scp/local").mkdirs();
379
        scp.download("target/scp/remote/*", "target/scp/local", ScpClient.Option.Recursive, ScpClient.Option.PreserveAttributes);
380
        assertFileLength(new File("target/scp/local/out1.txt"), data.length(), 5000);
381
        assertEquals(lastMod, new File("target/scp/local/out1.txt").lastModified());
382
        assertFileLength(new File("target/scp/local/dir/out2.txt"), data.length(), 5000);
383
        assertEquals(lastMod, new File("target/scp/local/dir/out2.txt").lastModified());
384
 
385
        session.close(false).await();
386
        client.stop();
387
    }
388
 
389
    private void writeFile(File file, String data) throws IOException {
390
        FileOutputStream fos = new FileOutputStream(file);
391
        try {
392
            fos.write(data.getBytes());
393
        } finally {
394
            fos.close();
395
        }
396
    }
397
 
398
    @Test
399
    public void testScp() throws Exception {
400
        session = getJschSession();
401
 
402
        String data = "0123456789\n";
403
 
404
        String unixDir = "target/scp";
405
        String fileName = "out.txt";
406
        String unixPath = unixDir + File.separator + fileName;
407
        File root = new File(unixDir);
408
        File target = new File(unixPath);
409
        Utils.deleteRecursive(root);
410
        root.mkdirs();
411
        assertTrue(root.exists());
412
 
413
        target.delete();
414
        assertFalse(target.exists());
415
        sendFile(unixPath, "out.txt", data);
416
        assertFileLength(target, data.length(), 5000);
417
 
418
        target.delete();
419
        assertFalse(target.exists());
420
        sendFile(unixDir, "out.txt", data);
421
        assertFileLength(target, data.length(), 5000);
422
 
423
        sendFileError("target", "scp", "0123456789\n");
424
 
425
        readFileError(unixDir);
426
 
427
        assertEquals(data, readFile(unixPath));
428
 
429
        assertEquals(data, readDir(unixDir));
430
 
431
        target.delete();
432
        root.delete();
433
 
434
        sendDir("target", "scp", "out.txt", data);
435
        assertFileLength(target, data.length(), 5000);
436
    }
437
 
438
    @Test
439
    public void testWithGanymede() throws Exception {
440
        // begin client config
441
        final Connection conn = new Connection("localhost", port);
442
        conn.connect(null, 5000, 0);
443
        conn.authenticateWithPassword("sshd", "sshd");
444
        final SCPClient scp_client = new SCPClient(conn);
445
        final Properties props = new Properties();
446
        props.setProperty("test", "test-passed");
447
        File f = new File("target/scp/gan");
448
        Utils.deleteRecursive(f);
449
        f.mkdirs();
450
        assertTrue(f.exists());
451
 
452
        String name = "test.properties";
453
        scp_client.put(toBytes(props, ""), name, "target/scp/gan");
454
        assertTrue(new File(f, name).exists());
455
        assertTrue(new File(f, name).delete());
456
 
457
        name = "test2.properties";
458
        scp_client.put(toBytes(props, ""), name, "target/scp/gan");
459
        assertTrue(new File(f, name).exists());
460
        assertTrue(new File(f, name).delete());
461
 
462
        assertTrue(f.delete());
463
        conn.close();
464
    }
465
 
466
    private byte[] toBytes(final Properties properties, final String comments) {
467
        try {
468
            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
469
            properties.store(baos, comments);
470
            baos.close();
471
            return baos.toByteArray();
472
        } catch (final IOException cause) {
473
            throw new RuntimeException("Failed to output properties to byte[]", cause);
474
        }
475
    }
476
 
477
    protected void assertFileLength(File file, long length, long timeout) throws Exception{
478
        boolean ok = false;
479
        while (timeout > 0) {
480
            if (file.exists() && file.length() == length) {
481
                if (!ok) {
482
                    ok = true;
483
                } else {
484
                    return;
485
                }
486
            } else {
487
                ok = false;
488
            }
489
            Thread.sleep(100);
490
            timeout -= 100;
491
        }
492
        assertTrue(file.exists());
493
        assertEquals(length, file.length());
494
    }
495
 
496
    protected String readFile(String path) throws Exception {
497
        ChannelExec c = (ChannelExec) session.openChannel("exec");
498
        OutputStream os = c.getOutputStream();
499
        InputStream is = c.getInputStream();
500
        c.setCommand("scp -f " + path);
501
        c.connect();
502
        os.write(0);
503
        os.flush();
504
        String header = readLine(is);
505
        assertEquals("C0644 11 out.txt", header);
506
        int length = Integer.parseInt(header.substring(6, header.indexOf(' ', 6)));
507
        os.write(0);
508
        os.flush();
509
 
510
        byte[] buffer = new byte[length];
511
        length = is.read(buffer, 0, buffer.length);
512
        assertEquals(length, buffer.length);
513
        assertEquals(0, is.read());
514
        os.write(0);
515
        os.flush();
516
 
517
        c.disconnect();
518
        return new String(buffer);
519
    }
520
 
521
    protected String readDir(String path) throws Exception {
522
        ChannelExec c = (ChannelExec) session.openChannel("exec");
523
        OutputStream os = c.getOutputStream();
524
        InputStream is = c.getInputStream();
525
        c.setCommand("scp -r -f " + path);
526
        c.connect();
527
        os.write(0);
528
        os.flush();
529
        String header = readLine(is);
530
        assertTrue(header.startsWith("D0755 0 "));
531
        os.write(0);
532
        os.flush();
533
        header = readLine(is);
534
        assertEquals("C0644 11 out.txt", header);
535
        int length = Integer.parseInt(header.substring(6, header.indexOf(' ', 6)));
536
        os.write(0);
537
        os.flush();
538
        byte[] buffer = new byte[length];
539
        length = is.read(buffer, 0, buffer.length);
540
        assertEquals(length, buffer.length);
541
        assertEquals(0, is.read());
542
        os.write(0);
543
        os.flush();
544
        header = readLine(is);
545
        assertEquals("E", header);
546
        os.write(0);
547
        os.flush();
548
 
549
        c.disconnect();
550
        return new String(buffer);
551
    }
552
 
553
    protected String readFileError(String path) throws Exception {
554
        ChannelExec c = (ChannelExec) session.openChannel("exec");
555
        OutputStream os = c.getOutputStream();
556
        InputStream is = c.getInputStream();
557
        c.setCommand("scp -f " + path);
558
        c.connect();
559
        os.write(0);
560
        os.flush();
561
        assertEquals(2, is.read());
562
        c.disconnect();
563
        return null;
564
    }
565
 
566
    protected void sendFile(String path, String name, String data) throws Exception {
567
        ChannelExec c = (ChannelExec) session.openChannel("exec");
568
        c.setCommand("scp -t " + path);
569
        OutputStream os = c.getOutputStream();
570
        InputStream is = c.getInputStream();
571
        c.connect();
572
        assertEquals(0, is.read());
573
        os.write(("C7777 "+ data.length() + " " + name + "\n").getBytes());
574
        os.flush();
575
        assertEquals(0, is.read());
576
        os.write(data.getBytes());
577
        os.flush();
578
        assertEquals(0, is.read());
579
        os.write(0);
580
        os.flush();
581
        Thread.sleep(100);
582
        c.disconnect();
583
    }
584
 
585
    protected void sendFileError(String path, String name, String data) throws Exception {
586
        ChannelExec c = (ChannelExec) session.openChannel("exec");
587
        OutputStream os = c.getOutputStream();
588
        InputStream is = c.getInputStream();
589
        c.setCommand("scp -t " + path);
590
        c.connect();
591
        assertEquals(0, is.read());
592
        os.write(("C7777 "+ data.length() + " " + name + "\n").getBytes());
593
        os.flush();
594
        assertEquals(2, is.read());
595
        c.disconnect();
596
    }
597
 
598
    protected void sendDir(String path, String dirName, String fileName, String data) throws Exception {
599
        ChannelExec c = (ChannelExec) session.openChannel("exec");
600
        OutputStream os = c.getOutputStream();
601
        InputStream is = c.getInputStream();
602
        c.setCommand("scp -t -r " + path);
603
        c.connect();
604
        assertEquals(0, is.read());
605
        os.write(("D0755 0 " + dirName + "\n").getBytes());
606
        os.flush();
607
        assertEquals(0, is.read());
608
        os.write(("C7777 " + data.length() + " " + fileName + "\n").getBytes());
609
        os.flush();
610
        assertEquals(0, is.read());
611
        os.write(data.getBytes());
612
        os.flush();
613
        assertEquals(0, is.read());
614
        os.write(0);
615
        os.flush();
616
        os.write("E\n".getBytes());
617
        os.flush();
618
        assertEquals(0, is.read());
619
    }
620
 
621
    private String readLine(InputStream in) throws IOException {
622
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
623
        for (;;) {
624
            int c = in.read();
625
            if (c == '\n') {
626
                return baos.toString();
627
            } else if (c == -1) {
628
                throw new IOException("End of stream");
629
            } else {
630
                baos.write(c);
631
            }
632
        }
633
    }
634
 
635
}