View Javadoc

1   /*
2    * ------------------------------------------------------------------------------
3    * Hermes FTP Server
4    * Copyright (c) 2005-2007 Lars Behnke
5    * ------------------------------------------------------------------------------
6    * 
7    * This file is part of Hermes FTP Server.
8    * 
9    * Hermes FTP Server is free software; you can redistribute it and/or modify
10   * it under the terms of the GNU General Public License as published by
11   * the Free Software Foundation; either version 2 of the License, or
12   * (at your option) any later version.
13   * 
14   * Hermes FTP Server is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   * 
19   * You should have received a copy of the GNU General Public License
20   * along with Hermes FTP Server; if not, write to the Free Software
21   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22   * ------------------------------------------------------------------------------
23   */
24  
25  package net.sf.hermesftp.client;
26  
27  import java.io.BufferedOutputStream;
28  import java.io.BufferedReader;
29  import java.io.BufferedWriter;
30  import java.io.ByteArrayOutputStream;
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.io.InputStreamReader;
34  import java.io.OutputStream;
35  import java.io.OutputStreamWriter;
36  import java.io.PrintWriter;
37  import java.net.InetAddress;
38  import java.net.ServerSocket;
39  import java.net.Socket;
40  import java.util.StringTokenizer;
41  import java.util.regex.Matcher;
42  import java.util.regex.Pattern;
43  
44  import javax.net.ServerSocketFactory;
45  
46  import net.sf.hermesftp.common.FtpConstants;
47  import net.sf.hermesftp.utils.NetUtils;
48  
49  import org.apache.commons.logging.Log;
50  import org.apache.commons.logging.LogFactory;
51  
52  // CHECKSTYLE:OFF
53  /***
54   * FTP test client for test purposes.
55   * 
56   * @author Lars Behnke
57   */
58  public class FtpTestClient {
59  
60      /***
61       * On Linux ports below 1024 can only be bound by root.
62       */
63      private static final int TEST_FTP_PORT   = 2121;
64  
65      private static final int LOG_LINE_LENGTH = 80;
66  
67      private static Log       log             = LogFactory.getLog(FtpTestClient.class);
68  
69      private PrintWriter      out;
70  
71      private BufferedReader   in;
72  
73      private String           server;
74  
75      private InputStream      transIs;
76  
77      private OutputStream     transOut;
78  
79      private Socket           passiveModeSocket;
80  
81      private ServerSocket     activeModeServerSocket;
82  
83      private Socket           serverSocket;
84  
85      private StringBuffer     textBuffer;
86  
87      private byte[]           rawBuffer;
88  
89      private Object           lock            = new Object();
90  
91      /***
92       * Returns the text data.
93       * 
94       * @return The text data.
95       */
96      public String getTextData() {
97          return textBuffer.toString();
98      }
99  
100     /***
101      * Returns the raw data.
102      * 
103      * @return The text data.
104      */
105     public byte[] getRawData() {
106         return rawBuffer;
107     }
108 
109     /***
110      * Opens a anonymous FTP connection.
111      * 
112      * @throws IOException Error on connection.
113      */
114     public void openConnection() throws IOException {
115         openConnection("anonymous", "my@mail");
116     }
117 
118     /***
119      * Opens a FTP connection.
120      * 
121      * @param user The user name.
122      * @param pass The user password.
123      * @throws IOException Error on connection.
124      */
125     public void openConnection(String user, String pass) throws IOException {
126         openConnection(null, user, pass, TEST_FTP_PORT);
127 
128     }
129 
130     /***
131      * Closes the FTP connection.
132      */
133     public void closeConnection() {
134         try {
135             if (serverSocket != null) {
136                 serverSocket.close();
137             }
138 
139         } catch (IOException e) {
140             log.debug(e.toString());
141         }
142         try {
143             if (passiveModeSocket != null) {
144                 passiveModeSocket.close();
145             }
146 
147         } catch (IOException e) {
148             log.debug(e.toString());
149         }
150     }
151 
152     /***
153      * Opening a connection to the FTP server.
154      * 
155      * @param svr The server name. If null is passed, the local machine is used.
156      * @param user The user name.
157      * @param pass The user password.
158      * @throws IOException Error on connection.
159      */
160     public void openConnection(String svr, String user, String pass) throws IOException {
161         openConnection(svr, user, pass, TEST_FTP_PORT);
162     }
163 
164     /***
165      * Opening a connection to the FTP server.
166      * 
167      * @param svr The server name. If null is passed, the local machine is used.
168      * @param user The user name.
169      * @param pass The user password.
170      * @param port FTP port.
171      * @throws IOException Error on connection.
172      */
173     public void openConnection(String svr, String user, String pass, int port) throws IOException {
174         this.server = svr;
175 
176         if (server == null || server.startsWith("127.0.0.")) {
177             this.server = NetUtils.getMachineAddress(true).getHostAddress();
178         }
179         serverSocket = new Socket(server, port);
180         in = new BufferedReader(new InputStreamReader(serverSocket.getInputStream()));
181         out = new PrintWriter(serverSocket.getOutputStream(), true);
182         getResponse();
183         sendAndReceive("USER " + user);
184         sendAndReceive("PASS " + pass);
185     }
186 
187     public String openPassiveMode() throws IOException {
188         sendCommand("PASV");
189         String response = getResponse();
190         int parentStart = response.lastIndexOf('(');
191         int parentEnd = response.lastIndexOf(')');
192 
193         String pasv = response.substring(parentStart + 1, parentEnd);
194         StringTokenizer st = new StringTokenizer(pasv, ",");
195         int[] iPs = new int[8];
196         for (int i = 0; st.hasMoreTokens(); i++) {
197             iPs[i] = Integer.valueOf(st.nextToken()).intValue();
198         }
199         int port = (iPs[4] << FtpConstants.BYTE_LENGTH) + iPs[5];
200 
201         resetDataSockets();
202         passiveModeSocket = new Socket(server, port);
203         return response;
204     }
205 
206     public String openActiveMode() throws IOException {
207         InetAddress addr = NetUtils.getMachineAddress(true);
208         String addrStr = addr != null ? addr.getHostAddress() : "127.0.0.1";
209         ServerSocket sock = ServerSocketFactory.getDefault().createServerSocket(0, 1, addr);
210         sock.setSoTimeout(10000);
211 
212         Pattern pattern = Pattern.compile("^([0-9]+)//.([0-9]+)//.([0-9]+)//.([0-9]+)$");
213         Matcher matcher = pattern.matcher(addrStr);
214         if (!matcher.matches()) {
215             throw new IOException("Invalid address: " + addrStr);
216         }
217         int p1 = (sock.getLocalPort() >>> FtpConstants.BYTE_LENGTH) & FtpConstants.BYTE_MASK;
218         int p2 = sock.getLocalPort() & FtpConstants.BYTE_MASK;
219 
220         StringBuffer sb = new StringBuffer();
221         sb.append("PORT ");
222         sb.append(matcher.group(1));
223         sb.append(",");
224         sb.append(matcher.group(2));
225         sb.append(",");
226         sb.append(matcher.group(3));
227         sb.append(",");
228         sb.append(matcher.group(4));
229         sb.append(",");
230         sb.append(p1);
231         sb.append(",");
232         sb.append(p2);
233 
234         resetDataSockets();
235         activeModeServerSocket = sock;
236 
237         sendCommand(sb.toString());
238 
239         //        
240         // try {
241         // passiveModeSocket = sock.accept();
242         // } catch (RuntimeException e) {
243         // throw new IOException("Accepting data channel failed");
244         // }
245 
246         String response = getResponse();
247 
248         // if (passiveModeSocket != null) {
249         // transIs = passiveModeSocket.getInputStream();
250         // transOut = passiveModeSocket.getOutputStream();
251         // }
252 
253         return response;
254 
255     }
256 
257     public String openExtendedPassiveMode() throws IOException {
258         sendCommand("EPSV");
259         String response = getResponse();
260 
261         Pattern pattern = Pattern.compile("^.*//(//|//|//|([0-9]+)//|//).*$");
262         Matcher matcher = pattern.matcher(response);
263         int port = 0;
264         if (matcher.matches()) {
265             port = Integer.parseInt(matcher.group(1));
266         }
267 
268         resetDataSockets();
269         passiveModeSocket = new Socket(server, port);
270         return response;
271     }
272 
273     private void initializeIOStreams() throws IOException {
274         if (passiveModeSocket != null) {
275             transIs = passiveModeSocket.getInputStream();
276             transOut = passiveModeSocket.getOutputStream();
277         } else if (activeModeServerSocket != null) {
278             Socket socket = activeModeServerSocket.accept();
279             transIs = socket.getInputStream();
280             transOut = socket.getOutputStream();
281         } else {
282             throw new IOException("IO streams have not been initialized");
283         }
284     }
285 
286     private boolean isServerSocketAvailable() {
287         return passiveModeSocket != null || activeModeServerSocket != null;
288     }
289 
290     private void resetDataSockets() throws IOException {
291         if (passiveModeSocket != null) {
292             passiveModeSocket.close();
293             passiveModeSocket = null;
294         }
295         if (activeModeServerSocket != null) {
296             activeModeServerSocket.close();
297             activeModeServerSocket = null;
298         }
299     }
300 
301     public String openExtendedActiveMode() throws IOException {
302         StringBuffer params = new StringBuffer();
303 
304         params.append("|1|");
305         InetAddress addr = NetUtils.getMachineAddress(true);
306         ServerSocket serverSocket = ServerSocketFactory.getDefault().createServerSocket(0, 1, addr);
307         params.append(addr.getHostAddress());
308         params.append("|");
309         params.append(serverSocket.getLocalPort());
310         params.append("|");
311 
312         sendCommand("EPRT " + params.toString());
313         String response = getResponse();
314 
315         if (passiveModeSocket != null) {
316             passiveModeSocket.close();
317         }
318         activeModeServerSocket = serverSocket;
319         return response;
320     }
321 
322     /***
323      * Lists the content of the passed path.
324      * 
325      * @param f The path.
326      * @return The content as string.
327      * @throws IOException Error on data transfer.
328      */
329     public String list(String f) throws IOException {
330         String response = null;
331         if (!isServerSocketAvailable()) {
332             openPassiveMode();
333         }
334         textBuffer = new StringBuffer();
335         if (f == null) {
336             sendCommand("LIST");
337         } else {
338             sendCommand("LIST " + f);
339         }
340         String res = getResponse();
341 
342         if (res.startsWith("150")) {
343             initializeIOStreams();
344         } else {
345             return res;
346         }
347 
348         BufferedReader in = new BufferedReader(new InputStreamReader(transIs, "ISO-8859-1"));
349         TextReceiver l = new TextReceiver(in, false);
350         executeReceive(l);
351         resetDataSockets();
352         response = getResponse();
353         return response;
354 
355     }
356 
357     /***
358      * Lists the current folder.
359      * 
360      * @return The content.
361      * @throws IOException Error on data transfer.
362      */
363     public String list() throws IOException {
364         return list(null);
365     }
366 
367     /***
368      * Retrieves a text file.
369      * 
370      * @param filename The filename.
371      * @return The content of the text file.
372      * @throws IOException Error on data transfer.
373      */
374     public String retrieveText(String filename) throws IOException {
375         String response = null;
376         sendAndReceive("TYPE A");
377         if (!isServerSocketAvailable()) {
378             openPassiveMode();
379         }
380         textBuffer = new StringBuffer();
381         response = sendAndReceive("RETR " + filename);
382         if (!response.startsWith("150")) {
383             return response;
384         }
385         initializeIOStreams();
386         BufferedReader in = new BufferedReader(new InputStreamReader(transIs, "ISO-8859-1"));
387         TextReceiver l = new TextReceiver(in, false);
388         executeReceive(l);
389 
390         response = getResponse();
391         resetDataSockets();
392         return response;
393     }
394 
395     /***
396      * Retrieves a text file.
397      * 
398      * @param filename The filename.
399      * @return Size of file.
400      * @throws IOException Error on data transfer.
401      */
402     public int retrieveBigText(String filename) throws IOException {
403 
404         String response;
405         sendAndReceive("TYPE A");
406         if (!isServerSocketAvailable()) {
407             openPassiveMode();
408         }
409         textBuffer = new StringBuffer();
410         response = sendAndReceive("RETR " + filename);
411         if (response.startsWith("150")) {
412             initializeIOStreams();
413         } else {
414             return 0;
415         }
416 
417         BufferedReader in = new BufferedReader(new InputStreamReader(transIs, "ISO-8859-1"));
418         TextReceiver l = new TextReceiver(in, true);
419         executeReceive(l);
420 
421         getResponse();
422         resetDataSockets();
423         return l.getCount();
424     }
425 
426 	private void executeReceive(TextReceiver l) {
427 		Thread t = new Thread(l);
428         t.start();
429         while (t.isAlive()) {
430         	Thread.yield();
431         }
432 	}
433 
434     /***
435      * Retrieves a raw data file.
436      * 
437      * @param filename The filename.
438      * @return The content of the data file.
439      * @throws IOException Error on data transfer.
440      */
441     public String retrieveRaw(String filename) throws IOException {
442         String response;
443         if (!isServerSocketAvailable()) {
444             openPassiveMode();
445         }
446         rawBuffer = null;
447         response = sendAndReceive("RETR " + filename);
448         if (!response.startsWith("150")) {
449             return response;
450         }
451         initializeIOStreams();
452         RawReceiver l = new RawReceiver(transIs);
453         executeReceive(l);
454         
455         response = getResponse();
456         resetDataSockets();
457         return response;
458     }
459 
460 	private void executeReceive(RawReceiver l) {
461 		Thread t = new Thread(l);
462         t.start();
463         while (t.isAlive()) {
464         	Thread.yield();
465         }
466 	}
467 
468     /***
469      * Stores a text file on the remote system.
470      * 
471      * @param filename The filename.
472      * @param textToStore The text to be stored.
473      * @return The response.
474      * @throws IOException Error on data transfer.
475      */
476     public String storeText(String filename, String textToStore) throws IOException {
477         return storeText(filename, textToStore, false);
478     }
479 
480     /***
481      * Appends text to an text file.
482      * 
483      * @param filename The filename.
484      * @param textToStore The text to append.
485      * @return The server response.
486      * @throws IOException Error on data transfer.
487      */
488     public String appendText(String filename, String textToStore) throws IOException {
489         return storeText(filename, textToStore, true);
490     }
491 
492     private String storeText(String filename, String textToStore, boolean append) throws IOException {
493         String response = null;
494 
495         sendAndReceive("TYPE A");
496         if (!isServerSocketAvailable()) {
497             openPassiveMode();
498         }
499 
500         textBuffer = new StringBuffer();
501         if (append) {
502             sendCommand("APPE" + filename);
503         } else {
504             sendCommand("STOR " + filename);
505         }
506         response = getResponse();
507         if (!response.startsWith("150")) {
508             return response;
509         }
510         initializeIOStreams();
511         BufferedWriter out = new BufferedWriter(new OutputStreamWriter(transOut, "ISO-8859-1"));
512         TextSender l = new TextSender(out, textToStore);
513         executeSend(l);
514         
515         response = getResponse();
516         resetDataSockets();
517         return response;
518 
519     }
520 
521     /***
522      * Stores a file of a given size. The content is arbitrary.
523      * 
524      * @param filename Filename.
525      * @param size Size of the file.
526      * @return Response.
527      * @throws IOException
528      */
529     public String storeBigText(String filename, int size) throws IOException {
530         String response = null;
531         // openPassiveMode();
532         response = sendAndReceive("TYPE A");
533         if (!isServerSocketAvailable()) {
534             openPassiveMode();
535         }
536         textBuffer = new StringBuffer();
537         sendCommand("STOR " + filename);
538 
539         response = getResponse();
540         if (!response.startsWith("150")) {
541             return response;
542         }
543         initializeIOStreams();
544         BufferedWriter out = new BufferedWriter(new OutputStreamWriter(transOut, "ISO-8859-1"));
545         TextSender l = new TextSender(out, size);
546         executeSend(l);
547         response = getResponse();
548         resetDataSockets();
549         return response;
550 
551     }
552 
553 	private void executeSend(TextSender l) {
554 		Thread t = new Thread(l);
555         t.start();
556         while (t.isAlive()) {
557         	Thread.yield();
558         }
559 	}
560 
561     /***
562      * Stores a data file on the remote system.
563      * 
564      * @param filename The filename.
565      * @param data The Data to be stored.
566      * @return The response.
567      * @throws IOException Error on data transfer.
568      */
569     public String storeRaw(String filename, byte[] data) throws IOException {
570         return storeRaw(filename, data, false);
571     }
572 
573     /***
574      * Appends text to an data file.
575      * 
576      * @param filename The filename.
577      * @param data The data to append.
578      * @return The server response.
579      * @throws IOException Error on data transfer.
580      */
581     public String appendRaw(String filename, byte[] data) throws IOException {
582         return storeRaw(filename, data, true);
583     }
584 
585     private String storeRaw(String filename, byte[] data, boolean append) throws IOException {
586         String response = null;
587         //response = sendAndReceive("TYPE I");
588         if (!isServerSocketAvailable()) {
589             openPassiveMode();
590         }
591         if (append) {
592             sendCommand("APPE" + filename);
593         } else {
594             sendCommand("STOR " + filename);
595         }
596         response = getResponse();
597         if (!response.startsWith("150")) {
598             return response;
599         }
600         initializeIOStreams();
601 
602         BufferedOutputStream out = new BufferedOutputStream(transOut);
603         RawSender l = new RawSender(out, data);
604         executeSend(l);
605         response = getResponse();
606         resetDataSockets();
607         return response;
608 
609     }
610 
611 	private void executeSend(RawSender l) {
612 		Thread t = new Thread(l);
613         t.start();
614         while (t.isAlive()) {
615         	Thread.yield();
616         }
617 	}
618 
619     /***
620      * Sends a command string to the server.
621      * 
622      * @param cmd The command.
623      * @return The server response.
624      * @throws IOException Error on data transfer.
625      */
626     public String sendAndReceive(String cmd) throws IOException {
627         sendCommand(cmd);
628         return getResponse();
629     }
630 
631     private String getResponse() throws IOException {
632         return getResponse(in);
633     }
634 
635     private String getResponse(BufferedReader in) throws IOException {
636         StringBuffer sb = new StringBuffer();
637         boolean done;
638         do {
639             String line = in.readLine();
640             sb.append(line + "\n");
641             int idx = 0;
642             done = Character.isDigit(line.charAt(idx++)) && Character.isDigit(line.charAt(idx++))
643                     && Character.isDigit(line.charAt(idx++)) && line.charAt(idx++) == ' ';
644         } while (!done);
645         return sb.toString().trim();
646     }
647 
648     /***
649      * Send command and wait for resonse.
650      */
651     private void sendCommand(String command) throws IOException {
652         // log.info("-> " + command);
653         out.println(command);
654     }
655 
656     /***
657      * Listens to server socket.
658      * 
659      * @author Lars Behnke
660      */
661     private class TextReceiver implements Runnable {
662 
663         private BufferedReader reader;
664 
665         private int            count;
666 
667         private boolean        countOnly;
668 
669         /***
670          * Constructor.
671          * 
672          * @param reader The reader.
673          * @param countOnly True, if only the text size matters.
674          */
675         public TextReceiver(BufferedReader reader, boolean countOnly) {
676             this.countOnly = countOnly;
677             this.reader = reader;
678         }
679 
680         /***
681          * {@inheritDoc}
682          */
683         public void run() {
684             synchronized (lock) {
685                 try {
686                     char[] buffer = new char[4096];
687                     int count;
688 
689                     while ((count = reader.read(buffer)) != -1) {
690 
691                         if (!countOnly) {
692                             String logLine = new String(buffer, 0, count);
693                             if (textBuffer.length() > 0) {
694                                 textBuffer.append(System.getProperty("line.separator"));
695                             }
696                             textBuffer.append(logLine);
697                             if (log.isTraceEnabled()) {
698                                 if (count >= LOG_LINE_LENGTH) {
699 
700                                     logLine = logLine.substring(0, LOG_LINE_LENGTH) + " ["
701                                             + (logLine.length() - LOG_LINE_LENGTH) + " chars more]";
702                                 }
703 
704                                 log.trace("<==: " + logLine.trim());
705                             }
706                         }
707                         this.count += count;
708                     }
709                     if (countOnly) {
710                         log.trace("<==: " + this.count + " characters read");
711                     }
712                     reader.close();
713 
714                 } catch (IOException e) {
715                     log.error(e);
716                 }
717                 lock.notifyAll();
718             }
719         }
720 
721         /***
722          * @return Size of file.
723          */
724         public int getCount() {
725             return count;
726         }
727     }
728 
729     /***
730      * Sends data to server socket.
731      * 
732      * @author Lars Behnke
733      */
734     private class TextSender implements Runnable {
735 
736         private BufferedWriter writer;
737 
738         private String         textToSend;
739 
740         private int            textSize;
741 
742         /***
743          * Constructor.
744          * 
745          * @param writer The writer.
746          * @param textToSend Text to send.
747          */
748         public TextSender(BufferedWriter writer, String textToSend) {
749             this.writer = writer;
750             this.textToSend = textToSend;
751             this.textSize = -1;
752         }
753 
754         /***
755          * Constructor.
756          * 
757          * @param writer The writer.
758          * @param size Size of the test string.
759          */
760         public TextSender(BufferedWriter writer, int size) {
761             this.writer = writer;
762             this.textSize = size;
763         }
764 
765         /***
766          * @see java.lang.Runnable#run()
767          */
768         public void run() {
769             synchronized (lock) {
770                 try {
771                     if (textSize == -1 && textToSend != null) {
772                         if (log.isTraceEnabled()) {
773                             String x;
774                             if (textToSend.length() >= LOG_LINE_LENGTH) {
775                                 x = textToSend.substring(0, LOG_LINE_LENGTH) + " ["
776                                         + (textToSend.length() - LOG_LINE_LENGTH) + " chars more]";
777                             } else {
778                                 x = textToSend;
779                             }
780                             log.trace("==>: " + x.trim());
781                         }
782                         writer.write(textToSend);
783                         writer.flush();
784                     } else if (textSize >= 0) {
785                         if (log.isTraceEnabled()) {
786                             log.trace("==>: Sending test text, length: " + textSize);
787                         }
788                         for (int i = 0; i < textSize; i++) {
789                             writer.write('X');
790                         }
791 
792                     }
793 
794                     writer.close();
795                 } catch (IOException e) {
796                     log.error(e);
797                 }
798                 lock.notifyAll();
799             }
800         }
801     }
802 
803     /***
804      * Sends data to server socket.
805      * 
806      * @author Lars Behnke
807      */
808     private class RawSender implements Runnable {
809 
810         private BufferedOutputStream os;
811 
812         private byte[]               dataToSend;
813 
814         /***
815          * Constructor.
816          * 
817          * @param os The output stream.
818          * @param dataToSend Data to send.
819          */
820         public RawSender(BufferedOutputStream os, byte[] dataToSend) {
821             this.os = os;
822             this.dataToSend = dataToSend;
823         }
824 
825         /***
826          * @see java.lang.Runnable#run()
827          */
828         public void run() {
829             synchronized (lock) {
830                 try {
831                     log.trace("==>: " + dataToSend.length + " bytes");
832                     os.write(dataToSend);
833                     os.flush();
834                     os.close();
835                 } catch (IOException e) {
836                     log.error(e);
837                 } finally {
838                     lock.notifyAll();
839                 }
840             }
841         }
842     }
843 
844     /***
845      * Raw data receiver that listens to server socket.
846      * 
847      * @author Lars Behnke
848      */
849     private class RawReceiver implements Runnable {
850 
851         private InputStream is;
852 
853         /***
854          * Constructor.
855          * 
856          * @param is The input stream.
857          */
858         public RawReceiver(InputStream is) {
859             this.is = is;
860         }
861 
862         /***
863          * {@inheritDoc}
864          */
865         public void run() {
866             ByteArrayOutputStream baos = new ByteArrayOutputStream();
867             byte[] buffer = new byte[1024];
868             int count = 0;
869             synchronized (lock) {
870                 try {
871 
872                     while ((count = is.read(buffer)) >= 0) {
873                         log.trace("<==: " + count + " bytes");
874                         baos.write(buffer, 0, count);
875                     }
876                     rawBuffer = baos.toByteArray();
877                 } catch (IOException e) {
878                     log.error(e);
879                 } finally {
880                     lock.notifyAll();
881                 }
882             }
883         }
884     }
885 
886 }
887 
888 // CHECKSTYLE:ON